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"]
1568 test "returns an empty list on a bad request", %{conn: conn} do
1569 user = insert(:user)
1573 |> assign(:user, user)
1574 |> get("/api/v1/accounts/relationships", %{})
1576 assert [] = json_response(conn, 200)
1580 describe "media upload" do
1582 user = insert(:user)
1586 |> assign(:user, user)
1588 image = %Plug.Upload{
1589 content_type: "image/jpg",
1590 path: Path.absname("test/fixtures/image.jpg"),
1591 filename: "an_image.jpg"
1594 [conn: conn, image: image]
1597 clear_config([:media_proxy])
1598 clear_config([Pleroma.Upload])
1600 test "returns uploaded image", %{conn: conn, image: image} do
1601 desc = "Description of the image"
1605 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1606 |> json_response(:ok)
1608 assert media["type"] == "image"
1609 assert media["description"] == desc
1612 object = Repo.get(Object, media["id"])
1613 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1617 describe "locked accounts" do
1618 test "/api/v1/follow_requests works" do
1619 user = insert(:user, %{info: %User.Info{locked: true}})
1620 other_user = insert(:user)
1622 {:ok, _activity} = ActivityPub.follow(other_user, user)
1624 user = User.get_cached_by_id(user.id)
1625 other_user = User.get_cached_by_id(other_user.id)
1627 assert User.following?(other_user, user) == false
1631 |> assign(:user, user)
1632 |> get("/api/v1/follow_requests")
1634 assert [relationship] = json_response(conn, 200)
1635 assert to_string(other_user.id) == relationship["id"]
1638 test "/api/v1/follow_requests/:id/authorize works" do
1639 user = insert(:user, %{info: %User.Info{locked: true}})
1640 other_user = insert(:user)
1642 {:ok, _activity} = ActivityPub.follow(other_user, user)
1644 user = User.get_cached_by_id(user.id)
1645 other_user = User.get_cached_by_id(other_user.id)
1647 assert User.following?(other_user, user) == false
1651 |> assign(:user, user)
1652 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1654 assert relationship = json_response(conn, 200)
1655 assert to_string(other_user.id) == relationship["id"]
1657 user = User.get_cached_by_id(user.id)
1658 other_user = User.get_cached_by_id(other_user.id)
1660 assert User.following?(other_user, user) == true
1663 test "verify_credentials", %{conn: conn} do
1664 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1668 |> assign(:user, user)
1669 |> get("/api/v1/accounts/verify_credentials")
1671 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1672 assert id == to_string(user.id)
1675 test "/api/v1/follow_requests/:id/reject works" do
1676 user = insert(:user, %{info: %User.Info{locked: true}})
1677 other_user = insert(:user)
1679 {:ok, _activity} = ActivityPub.follow(other_user, user)
1681 user = User.get_cached_by_id(user.id)
1685 |> assign(:user, user)
1686 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1688 assert relationship = json_response(conn, 200)
1689 assert to_string(other_user.id) == relationship["id"]
1691 user = User.get_cached_by_id(user.id)
1692 other_user = User.get_cached_by_id(other_user.id)
1694 assert User.following?(other_user, user) == false
1698 describe "account fetching" do
1699 test "works by id" do
1700 user = insert(:user)
1704 |> get("/api/v1/accounts/#{user.id}")
1706 assert %{"id" => id} = json_response(conn, 200)
1707 assert id == to_string(user.id)
1711 |> get("/api/v1/accounts/-1")
1713 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1716 test "works by nickname" do
1717 user = insert(:user)
1721 |> get("/api/v1/accounts/#{user.nickname}")
1723 assert %{"id" => id} = json_response(conn, 200)
1724 assert id == user.id
1727 test "works by nickname for remote users" do
1728 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1729 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1730 user = insert(:user, nickname: "user@example.com", local: false)
1734 |> get("/api/v1/accounts/#{user.nickname}")
1736 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1737 assert %{"id" => id} = json_response(conn, 200)
1738 assert id == user.id
1741 test "respects limit_to_local_content == :all for remote user nicknames" do
1742 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1743 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1745 user = insert(:user, nickname: "user@example.com", local: false)
1749 |> get("/api/v1/accounts/#{user.nickname}")
1751 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1752 assert json_response(conn, 404)
1755 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1756 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1757 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1759 user = insert(:user, nickname: "user@example.com", local: false)
1760 reading_user = insert(:user)
1764 |> get("/api/v1/accounts/#{user.nickname}")
1766 assert json_response(conn, 404)
1770 |> assign(:user, reading_user)
1771 |> get("/api/v1/accounts/#{user.nickname}")
1773 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1774 assert %{"id" => id} = json_response(conn, 200)
1775 assert id == user.id
1779 describe "/api/v1/pleroma/mascot" do
1780 test "mascot upload", %{conn: conn} do
1781 user = insert(:user)
1783 non_image_file = %Plug.Upload{
1784 content_type: "audio/mpeg",
1785 path: Path.absname("test/fixtures/sound.mp3"),
1786 filename: "sound.mp3"
1791 |> assign(:user, user)
1792 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1794 assert json_response(conn, 415)
1796 file = %Plug.Upload{
1797 content_type: "image/jpg",
1798 path: Path.absname("test/fixtures/image.jpg"),
1799 filename: "an_image.jpg"
1804 |> assign(:user, user)
1805 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1807 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1810 test "mascot retrieving", %{conn: conn} do
1811 user = insert(:user)
1812 # When user hasn't set a mascot, we should just get pleroma tan back
1815 |> assign(:user, user)
1816 |> get("/api/v1/pleroma/mascot")
1818 assert %{"url" => url} = json_response(conn, 200)
1819 assert url =~ "pleroma-fox-tan-smol"
1821 # When a user sets their mascot, we should get that back
1822 file = %Plug.Upload{
1823 content_type: "image/jpg",
1824 path: Path.absname("test/fixtures/image.jpg"),
1825 filename: "an_image.jpg"
1830 |> assign(:user, user)
1831 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1833 assert json_response(conn, 200)
1835 user = User.get_cached_by_id(user.id)
1839 |> assign(:user, user)
1840 |> get("/api/v1/pleroma/mascot")
1842 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1843 assert url =~ "an_image"
1847 test "hashtag timeline", %{conn: conn} do
1848 following = insert(:user)
1851 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1853 {:ok, [_activity]} =
1854 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1858 |> get("/api/v1/timelines/tag/2hu")
1860 assert [%{"id" => id}] = json_response(nconn, 200)
1862 assert id == to_string(activity.id)
1864 # works for different capitalization too
1867 |> get("/api/v1/timelines/tag/2HU")
1869 assert [%{"id" => id}] = json_response(nconn, 200)
1871 assert id == to_string(activity.id)
1875 test "multi-hashtag timeline", %{conn: conn} do
1876 user = insert(:user)
1878 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1879 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1880 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1884 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1886 [status_none, status_test1, status_test] = json_response(any_test, 200)
1888 assert to_string(activity_test.id) == status_test["id"]
1889 assert to_string(activity_test1.id) == status_test1["id"]
1890 assert to_string(activity_none.id) == status_none["id"]
1894 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1896 assert [status_test1] == json_response(restricted_test, 200)
1898 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1900 assert [status_none] == json_response(all_test, 200)
1903 test "getting followers", %{conn: conn} do
1904 user = insert(:user)
1905 other_user = insert(:user)
1906 {:ok, user} = User.follow(user, other_user)
1910 |> get("/api/v1/accounts/#{other_user.id}/followers")
1912 assert [%{"id" => id}] = json_response(conn, 200)
1913 assert id == to_string(user.id)
1916 test "getting followers, hide_followers", %{conn: conn} do
1917 user = insert(:user)
1918 other_user = insert(:user, %{info: %{hide_followers: true}})
1919 {:ok, _user} = User.follow(user, other_user)
1923 |> get("/api/v1/accounts/#{other_user.id}/followers")
1925 assert [] == json_response(conn, 200)
1928 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1929 user = insert(:user)
1930 other_user = insert(:user, %{info: %{hide_followers: true}})
1931 {:ok, _user} = User.follow(user, other_user)
1935 |> assign(:user, other_user)
1936 |> get("/api/v1/accounts/#{other_user.id}/followers")
1938 refute [] == json_response(conn, 200)
1941 test "getting followers, pagination", %{conn: conn} do
1942 user = insert(:user)
1943 follower1 = insert(:user)
1944 follower2 = insert(:user)
1945 follower3 = insert(:user)
1946 {:ok, _} = User.follow(follower1, user)
1947 {:ok, _} = User.follow(follower2, user)
1948 {:ok, _} = User.follow(follower3, user)
1952 |> assign(:user, user)
1956 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1958 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1959 assert id3 == follower3.id
1960 assert id2 == follower2.id
1964 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1966 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1967 assert id2 == follower2.id
1968 assert id1 == follower1.id
1972 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1974 assert [%{"id" => id2}] = json_response(res_conn, 200)
1975 assert id2 == follower2.id
1977 assert [link_header] = get_resp_header(res_conn, "link")
1978 assert link_header =~ ~r/min_id=#{follower2.id}/
1979 assert link_header =~ ~r/max_id=#{follower2.id}/
1982 test "getting following", %{conn: conn} do
1983 user = insert(:user)
1984 other_user = insert(:user)
1985 {:ok, user} = User.follow(user, other_user)
1989 |> get("/api/v1/accounts/#{user.id}/following")
1991 assert [%{"id" => id}] = json_response(conn, 200)
1992 assert id == to_string(other_user.id)
1995 test "getting following, hide_follows", %{conn: conn} do
1996 user = insert(:user, %{info: %{hide_follows: true}})
1997 other_user = insert(:user)
1998 {:ok, user} = User.follow(user, other_user)
2002 |> get("/api/v1/accounts/#{user.id}/following")
2004 assert [] == json_response(conn, 200)
2007 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2008 user = insert(:user, %{info: %{hide_follows: true}})
2009 other_user = insert(:user)
2010 {:ok, user} = User.follow(user, other_user)
2014 |> assign(:user, user)
2015 |> get("/api/v1/accounts/#{user.id}/following")
2017 refute [] == json_response(conn, 200)
2020 test "getting following, pagination", %{conn: conn} do
2021 user = insert(:user)
2022 following1 = insert(:user)
2023 following2 = insert(:user)
2024 following3 = insert(:user)
2025 {:ok, _} = User.follow(user, following1)
2026 {:ok, _} = User.follow(user, following2)
2027 {:ok, _} = User.follow(user, following3)
2031 |> assign(:user, user)
2035 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2037 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2038 assert id3 == following3.id
2039 assert id2 == following2.id
2043 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2045 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2046 assert id2 == following2.id
2047 assert id1 == following1.id
2051 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2053 assert [%{"id" => id2}] = json_response(res_conn, 200)
2054 assert id2 == following2.id
2056 assert [link_header] = get_resp_header(res_conn, "link")
2057 assert link_header =~ ~r/min_id=#{following2.id}/
2058 assert link_header =~ ~r/max_id=#{following2.id}/
2061 test "following / unfollowing a user", %{conn: conn} do
2062 user = insert(:user)
2063 other_user = insert(:user)
2067 |> assign(:user, user)
2068 |> post("/api/v1/accounts/#{other_user.id}/follow")
2070 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2072 user = User.get_cached_by_id(user.id)
2076 |> assign(:user, user)
2077 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2079 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2081 user = User.get_cached_by_id(user.id)
2085 |> assign(:user, user)
2086 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2088 assert %{"id" => id} = json_response(conn, 200)
2089 assert id == to_string(other_user.id)
2092 test "following without reblogs" do
2093 follower = insert(:user)
2094 followed = insert(:user)
2095 other_user = insert(:user)
2099 |> assign(:user, follower)
2100 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2102 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2104 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2105 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2109 |> assign(:user, User.get_cached_by_id(follower.id))
2110 |> get("/api/v1/timelines/home")
2112 assert [] == json_response(conn, 200)
2116 |> assign(:user, follower)
2117 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2119 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2123 |> assign(:user, User.get_cached_by_id(follower.id))
2124 |> get("/api/v1/timelines/home")
2126 expected_activity_id = reblog.id
2127 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2130 test "following / unfollowing errors" do
2131 user = insert(:user)
2135 |> assign(:user, user)
2138 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2139 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2142 user = User.get_cached_by_id(user.id)
2143 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2144 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2146 # self follow via uri
2147 user = User.get_cached_by_id(user.id)
2148 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2149 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2151 # follow non existing user
2152 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2153 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2155 # follow non existing user via uri
2156 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2157 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2159 # unfollow non existing user
2160 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2161 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2164 describe "mute/unmute" do
2165 test "with notifications", %{conn: conn} do
2166 user = insert(:user)
2167 other_user = insert(:user)
2171 |> assign(:user, user)
2172 |> post("/api/v1/accounts/#{other_user.id}/mute")
2174 response = json_response(conn, 200)
2176 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2177 user = User.get_cached_by_id(user.id)
2181 |> assign(:user, user)
2182 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2184 response = json_response(conn, 200)
2185 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2188 test "without notifications", %{conn: conn} do
2189 user = insert(:user)
2190 other_user = insert(:user)
2194 |> assign(:user, user)
2195 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2197 response = json_response(conn, 200)
2199 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2200 user = User.get_cached_by_id(user.id)
2204 |> assign(:user, user)
2205 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2207 response = json_response(conn, 200)
2208 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2212 describe "subscribing / unsubscribing" do
2213 test "subscribing / unsubscribing to a user", %{conn: conn} do
2214 user = insert(:user)
2215 subscription_target = insert(:user)
2219 |> assign(:user, user)
2220 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2222 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2226 |> assign(:user, user)
2227 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2229 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2233 describe "subscribing" do
2234 test "returns 404 when subscription_target not found", %{conn: conn} do
2235 user = insert(:user)
2239 |> assign(:user, user)
2240 |> post("/api/v1/pleroma/accounts/target_id/subscribe")
2242 assert %{"error" => "Record not found"} = json_response(conn, 404)
2246 describe "unsubscribing" do
2247 test "returns 404 when subscription_target not found", %{conn: conn} do
2248 user = insert(:user)
2252 |> assign(:user, user)
2253 |> post("/api/v1/pleroma/accounts/target_id/unsubscribe")
2255 assert %{"error" => "Record not found"} = json_response(conn, 404)
2259 test "getting a list of mutes", %{conn: conn} do
2260 user = insert(:user)
2261 other_user = insert(:user)
2263 {:ok, user} = User.mute(user, other_user)
2267 |> assign(:user, user)
2268 |> get("/api/v1/mutes")
2270 other_user_id = to_string(other_user.id)
2271 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2274 test "blocking / unblocking a user", %{conn: conn} do
2275 user = insert(:user)
2276 other_user = insert(:user)
2280 |> assign(:user, user)
2281 |> post("/api/v1/accounts/#{other_user.id}/block")
2283 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2285 user = User.get_cached_by_id(user.id)
2289 |> assign(:user, user)
2290 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2292 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2295 test "getting a list of blocks", %{conn: conn} do
2296 user = insert(:user)
2297 other_user = insert(:user)
2299 {:ok, user} = User.block(user, other_user)
2303 |> assign(:user, user)
2304 |> get("/api/v1/blocks")
2306 other_user_id = to_string(other_user.id)
2307 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2310 test "blocking / unblocking a domain", %{conn: conn} do
2311 user = insert(:user)
2312 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2316 |> assign(:user, user)
2317 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2319 assert %{} = json_response(conn, 200)
2320 user = User.get_cached_by_ap_id(user.ap_id)
2321 assert User.blocks?(user, other_user)
2325 |> assign(:user, user)
2326 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2328 assert %{} = json_response(conn, 200)
2329 user = User.get_cached_by_ap_id(user.ap_id)
2330 refute User.blocks?(user, other_user)
2333 test "getting a list of domain blocks", %{conn: conn} do
2334 user = insert(:user)
2336 {:ok, user} = User.block_domain(user, "bad.site")
2337 {:ok, user} = User.block_domain(user, "even.worse.site")
2341 |> assign(:user, user)
2342 |> get("/api/v1/domain_blocks")
2344 domain_blocks = json_response(conn, 200)
2346 assert "bad.site" in domain_blocks
2347 assert "even.worse.site" in domain_blocks
2350 test "unimplemented follow_requests, blocks, domain blocks" do
2351 user = insert(:user)
2353 ["blocks", "domain_blocks", "follow_requests"]
2354 |> Enum.each(fn endpoint ->
2357 |> assign(:user, user)
2358 |> get("/api/v1/#{endpoint}")
2360 assert [] = json_response(conn, 200)
2364 test "returns the favorites of a user", %{conn: conn} do
2365 user = insert(:user)
2366 other_user = insert(:user)
2368 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2369 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2371 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2375 |> assign(:user, user)
2376 |> get("/api/v1/favourites")
2378 assert [status] = json_response(first_conn, 200)
2379 assert status["id"] == to_string(activity.id)
2381 assert [{"link", _link_header}] =
2382 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2384 # Honours query params
2385 {:ok, second_activity} =
2386 CommonAPI.post(other_user, %{
2388 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2391 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2393 last_like = status["id"]
2397 |> assign(:user, user)
2398 |> get("/api/v1/favourites?since_id=#{last_like}")
2400 assert [second_status] = json_response(second_conn, 200)
2401 assert second_status["id"] == to_string(second_activity.id)
2405 |> assign(:user, user)
2406 |> get("/api/v1/favourites?limit=0")
2408 assert [] = json_response(third_conn, 200)
2411 describe "getting favorites timeline of specified user" do
2413 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2414 [current_user: current_user, user: user]
2417 test "returns list of statuses favorited by specified user", %{
2419 current_user: current_user,
2422 [activity | _] = insert_pair(:note_activity)
2423 CommonAPI.favorite(activity.id, user)
2427 |> assign(:user, current_user)
2428 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2429 |> json_response(:ok)
2433 assert length(response) == 1
2434 assert like["id"] == activity.id
2437 test "returns favorites for specified user_id when user is not logged in", %{
2441 activity = insert(:note_activity)
2442 CommonAPI.favorite(activity.id, user)
2446 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2447 |> json_response(:ok)
2449 assert length(response) == 1
2452 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2454 current_user: current_user,
2458 CommonAPI.post(current_user, %{
2459 "status" => "Hi @#{user.nickname}!",
2460 "visibility" => "direct"
2463 CommonAPI.favorite(direct.id, user)
2467 |> assign(:user, current_user)
2468 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2469 |> json_response(:ok)
2471 assert length(response) == 1
2473 anonymous_response =
2475 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2476 |> json_response(:ok)
2478 assert Enum.empty?(anonymous_response)
2481 test "does not return others' favorited DM when user is not one of recipients", %{
2483 current_user: current_user,
2486 user_two = insert(:user)
2489 CommonAPI.post(user_two, %{
2490 "status" => "Hi @#{user.nickname}!",
2491 "visibility" => "direct"
2494 CommonAPI.favorite(direct.id, user)
2498 |> assign(:user, current_user)
2499 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2500 |> json_response(:ok)
2502 assert Enum.empty?(response)
2505 test "paginates favorites using since_id and max_id", %{
2507 current_user: current_user,
2510 activities = insert_list(10, :note_activity)
2512 Enum.each(activities, fn activity ->
2513 CommonAPI.favorite(activity.id, user)
2516 third_activity = Enum.at(activities, 2)
2517 seventh_activity = Enum.at(activities, 6)
2521 |> assign(:user, current_user)
2522 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2523 since_id: third_activity.id,
2524 max_id: seventh_activity.id
2526 |> json_response(:ok)
2528 assert length(response) == 3
2529 refute third_activity in response
2530 refute seventh_activity in response
2533 test "limits favorites using limit parameter", %{
2535 current_user: current_user,
2539 |> insert_list(:note_activity)
2540 |> Enum.each(fn activity ->
2541 CommonAPI.favorite(activity.id, user)
2546 |> assign(:user, current_user)
2547 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2548 |> json_response(:ok)
2550 assert length(response) == 3
2553 test "returns empty response when user does not have any favorited statuses", %{
2555 current_user: current_user,
2560 |> assign(:user, current_user)
2561 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2562 |> json_response(:ok)
2564 assert Enum.empty?(response)
2567 test "returns 404 error when specified user is not exist", %{conn: conn} do
2568 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2570 assert json_response(conn, 404) == %{"error" => "Record not found"}
2573 test "returns 403 error when user has hidden own favorites", %{
2575 current_user: current_user
2577 user = insert(:user, %{info: %{hide_favorites: true}})
2578 activity = insert(:note_activity)
2579 CommonAPI.favorite(activity.id, user)
2583 |> assign(:user, current_user)
2584 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2586 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2589 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2590 user = insert(:user)
2591 activity = insert(:note_activity)
2592 CommonAPI.favorite(activity.id, user)
2596 |> assign(:user, current_user)
2597 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2599 assert user.info.hide_favorites
2600 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2604 test "get instance information", %{conn: conn} do
2605 conn = get(conn, "/api/v1/instance")
2606 assert result = json_response(conn, 200)
2608 email = Config.get([:instance, :email])
2609 # Note: not checking for "max_toot_chars" since it's optional
2615 "email" => from_config_email,
2617 "streaming_api" => _
2622 "registrations" => _,
2626 assert email == from_config_email
2629 test "get instance stats", %{conn: conn} do
2630 user = insert(:user, %{local: true})
2632 user2 = insert(:user, %{local: true})
2633 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2635 insert(:user, %{local: false, nickname: "u@peer1.com"})
2636 insert(:user, %{local: false, nickname: "u@peer2.com"})
2638 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2640 # Stats should count users with missing or nil `info.deactivated` value
2641 user = User.get_cached_by_id(user.id)
2642 info_change = Changeset.change(user.info, %{deactivated: nil})
2646 |> Changeset.change()
2647 |> Changeset.put_embed(:info, info_change)
2648 |> User.update_and_set_cache()
2650 Pleroma.Stats.force_update()
2652 conn = get(conn, "/api/v1/instance")
2654 assert result = json_response(conn, 200)
2656 stats = result["stats"]
2659 assert stats["user_count"] == 1
2660 assert stats["status_count"] == 1
2661 assert stats["domain_count"] == 2
2664 test "get peers", %{conn: conn} do
2665 insert(:user, %{local: false, nickname: "u@peer1.com"})
2666 insert(:user, %{local: false, nickname: "u@peer2.com"})
2668 Pleroma.Stats.force_update()
2670 conn = get(conn, "/api/v1/instance/peers")
2672 assert result = json_response(conn, 200)
2674 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2677 test "put settings", %{conn: conn} do
2678 user = insert(:user)
2682 |> assign(:user, user)
2683 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2685 assert _result = json_response(conn, 200)
2687 user = User.get_cached_by_ap_id(user.ap_id)
2688 assert user.info.settings == %{"programming" => "socks"}
2691 describe "pinned statuses" do
2693 user = insert(:user)
2694 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2696 [user: user, activity: activity]
2699 clear_config([:instance, :max_pinned_statuses]) do
2700 Config.put([:instance, :max_pinned_statuses], 1)
2703 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2704 {:ok, _} = CommonAPI.pin(activity.id, user)
2708 |> assign(:user, user)
2709 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2710 |> json_response(200)
2712 id_str = to_string(activity.id)
2714 assert [%{"id" => ^id_str, "pinned" => true}] = result
2717 test "pin status", %{conn: conn, user: user, activity: activity} do
2718 id_str = to_string(activity.id)
2720 assert %{"id" => ^id_str, "pinned" => true} =
2722 |> assign(:user, user)
2723 |> post("/api/v1/statuses/#{activity.id}/pin")
2724 |> json_response(200)
2726 assert [%{"id" => ^id_str, "pinned" => true}] =
2728 |> assign(:user, user)
2729 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2730 |> json_response(200)
2733 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2734 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2738 |> assign(:user, user)
2739 |> post("/api/v1/statuses/#{dm.id}/pin")
2741 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2744 test "unpin status", %{conn: conn, user: user, activity: activity} do
2745 {:ok, _} = CommonAPI.pin(activity.id, user)
2747 id_str = to_string(activity.id)
2748 user = refresh_record(user)
2750 assert %{"id" => ^id_str, "pinned" => false} =
2752 |> assign(:user, user)
2753 |> post("/api/v1/statuses/#{activity.id}/unpin")
2754 |> json_response(200)
2758 |> assign(:user, user)
2759 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2760 |> json_response(200)
2763 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2766 |> assign(:user, user)
2767 |> post("/api/v1/statuses/1/unpin")
2769 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2772 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2773 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2775 id_str_one = to_string(activity_one.id)
2777 assert %{"id" => ^id_str_one, "pinned" => true} =
2779 |> assign(:user, user)
2780 |> post("/api/v1/statuses/#{id_str_one}/pin")
2781 |> json_response(200)
2783 user = refresh_record(user)
2785 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2787 |> assign(:user, user)
2788 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2789 |> json_response(400)
2795 Config.put([:rich_media, :enabled], true)
2797 user = insert(:user)
2801 test "returns empty result when rich_media disabled", %{conn: conn, user: user} do
2802 Config.put([:rich_media, :enabled], false)
2803 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2807 |> get("/api/v1/statuses/#{activity.id}/card")
2808 |> json_response(200)
2810 assert response == nil
2813 test "returns rich-media card", %{conn: conn, user: user} do
2814 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2817 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2818 "provider_name" => "example.com",
2819 "provider_url" => "https://example.com",
2820 "title" => "The Rock",
2822 "url" => "https://example.com/ogp",
2824 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2827 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2828 "title" => "The Rock",
2829 "type" => "video.movie",
2830 "url" => "https://example.com/ogp",
2832 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2839 |> get("/api/v1/statuses/#{activity.id}/card")
2840 |> json_response(200)
2842 assert response == card_data
2844 # works with private posts
2846 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2850 |> assign(:user, user)
2851 |> get("/api/v1/statuses/#{activity.id}/card")
2852 |> json_response(200)
2854 assert response_two == card_data
2857 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2859 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2863 |> get("/api/v1/statuses/#{activity.id}/card")
2864 |> json_response(:ok)
2866 assert response == %{
2868 "title" => "Pleroma",
2869 "description" => "",
2871 "provider_name" => "example.com",
2872 "provider_url" => "https://example.com",
2873 "url" => "https://example.com/ogp-missing-data",
2876 "title" => "Pleroma",
2877 "type" => "website",
2878 "url" => "https://example.com/ogp-missing-data"
2884 test "returns 404 response when id invalid", %{conn: conn} do
2885 assert %{"error" => "Record not found"} =
2887 |> get("/api/v1/statuses/9eoozpwTul5mjSEDRI/card")
2888 |> json_response(404)
2891 test "returns 404 response when id isn't FlakeID", %{conn: conn} do
2892 assert %{"error" => "Record not found"} =
2894 |> get("/api/v1/statuses/3ebbadd1-eb14-4e20-8118/card")
2895 |> json_response(404)
2897 assert %{"error" => "Record not found"} =
2899 |> get("/api/v1/statuses/8118/card")
2900 |> json_response(404)
2905 user = insert(:user)
2906 for_user = insert(:user)
2909 CommonAPI.post(user, %{
2910 "status" => "heweoo?"
2914 CommonAPI.post(user, %{
2915 "status" => "heweoo!"
2920 |> assign(:user, for_user)
2921 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2923 assert json_response(response1, 200)["bookmarked"] == true
2927 |> assign(:user, for_user)
2928 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2930 assert json_response(response2, 200)["bookmarked"] == true
2934 |> assign(:user, for_user)
2935 |> get("/api/v1/bookmarks")
2937 assert [json_response(response2, 200), json_response(response1, 200)] ==
2938 json_response(bookmarks, 200)
2942 |> assign(:user, for_user)
2943 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2945 assert json_response(response1, 200)["bookmarked"] == false
2949 |> assign(:user, for_user)
2950 |> get("/api/v1/bookmarks")
2952 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2955 describe "conversation muting" do
2957 post_user = insert(:user)
2958 user = insert(:user)
2960 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2962 [user: user, activity: activity]
2965 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2966 id_str = to_string(activity.id)
2968 assert %{"id" => ^id_str, "muted" => true} =
2970 |> assign(:user, user)
2971 |> post("/api/v1/statuses/#{activity.id}/mute")
2972 |> json_response(200)
2975 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2976 {:ok, _} = CommonAPI.add_mute(user, activity)
2980 |> assign(:user, user)
2981 |> post("/api/v1/statuses/#{activity.id}/mute")
2983 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2986 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2987 {:ok, _} = CommonAPI.add_mute(user, activity)
2989 id_str = to_string(activity.id)
2990 user = refresh_record(user)
2992 assert %{"id" => ^id_str, "muted" => false} =
2994 |> assign(:user, user)
2995 |> post("/api/v1/statuses/#{activity.id}/unmute")
2996 |> json_response(200)
3000 describe "reports" do
3002 reporter = insert(:user)
3003 target_user = insert(:user)
3005 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
3007 [reporter: reporter, target_user: target_user, activity: activity]
3010 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
3011 assert %{"action_taken" => false, "id" => _} =
3013 |> assign(:user, reporter)
3014 |> post("/api/v1/reports", %{"account_id" => target_user.id})
3015 |> json_response(200)
3018 test "submit a report with statuses and comment", %{
3021 target_user: target_user,
3024 assert %{"action_taken" => false, "id" => _} =
3026 |> assign(:user, reporter)
3027 |> post("/api/v1/reports", %{
3028 "account_id" => target_user.id,
3029 "status_ids" => [activity.id],
3030 "comment" => "bad status!",
3031 "forward" => "false"
3033 |> json_response(200)
3036 test "account_id is required", %{
3041 assert %{"error" => "Valid `account_id` required"} =
3043 |> assign(:user, reporter)
3044 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3045 |> json_response(400)
3048 test "comment must be up to the size specified in the config", %{
3051 target_user: target_user
3053 max_size = Config.get([:instance, :max_report_comment_size], 1000)
3054 comment = String.pad_trailing("a", max_size + 1, "a")
3056 error = %{"error" => "Comment must be up to #{max_size} characters"}
3060 |> assign(:user, reporter)
3061 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3062 |> json_response(400)
3065 test "returns error when account is not exist", %{
3072 |> assign(:user, reporter)
3073 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3075 assert json_response(conn, 400) == %{"error" => "Account not found"}
3079 describe "link headers" do
3080 test "preserves parameters in link headers", %{conn: conn} do
3081 user = insert(:user)
3082 other_user = insert(:user)
3085 CommonAPI.post(other_user, %{
3086 "status" => "hi @#{user.nickname}",
3087 "visibility" => "public"
3091 CommonAPI.post(other_user, %{
3092 "status" => "hi @#{user.nickname}",
3093 "visibility" => "public"
3096 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3097 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3101 |> assign(:user, user)
3102 |> get("/api/v1/notifications", %{media_only: true})
3104 assert [link_header] = get_resp_header(conn, "link")
3105 assert link_header =~ ~r/media_only=true/
3106 assert link_header =~ ~r/min_id=#{notification2.id}/
3107 assert link_header =~ ~r/max_id=#{notification1.id}/
3111 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3112 # Need to set an old-style integer ID to reproduce the problem
3113 # (these are no longer assigned to new accounts but were preserved
3114 # for existing accounts during the migration to flakeIDs)
3115 user_one = insert(:user, %{id: 1212})
3116 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3120 |> get("/api/v1/accounts/#{user_one.id}")
3124 |> get("/api/v1/accounts/#{user_two.nickname}")
3128 |> get("/api/v1/accounts/#{user_two.id}")
3130 acc_one = json_response(resp_one, 200)
3131 acc_two = json_response(resp_two, 200)
3132 acc_three = json_response(resp_three, 200)
3133 refute acc_one == acc_two
3134 assert acc_two == acc_three
3137 describe "custom emoji" do
3138 test "with tags", %{conn: conn} do
3141 |> get("/api/v1/custom_emojis")
3142 |> json_response(200)
3144 assert Map.has_key?(emoji, "shortcode")
3145 assert Map.has_key?(emoji, "static_url")
3146 assert Map.has_key?(emoji, "tags")
3147 assert is_list(emoji["tags"])
3148 assert Map.has_key?(emoji, "category")
3149 assert Map.has_key?(emoji, "url")
3150 assert Map.has_key?(emoji, "visible_in_picker")
3154 describe "index/2 redirections" do
3155 setup %{conn: conn} do
3159 signing_salt: "cooldude"
3164 |> Plug.Session.call(Plug.Session.init(session_opts))
3167 test_path = "/web/statuses/test"
3168 %{conn: conn, path: test_path}
3171 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3172 conn = get(conn, path)
3174 assert conn.status == 302
3175 assert redirected_to(conn) == "/web/login"
3178 test "redirects not logged-in users to the login page on private instances", %{
3182 Config.put([:instance, :public], false)
3184 conn = get(conn, path)
3186 assert conn.status == 302
3187 assert redirected_to(conn) == "/web/login"
3190 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3191 token = insert(:oauth_token)
3195 |> assign(:user, token.user)
3196 |> put_session(:oauth_token, token.token)
3199 assert conn.status == 200
3202 test "saves referer path to session", %{conn: conn, path: path} do
3203 conn = get(conn, path)
3204 return_to = Plug.Conn.get_session(conn, :return_to)
3206 assert return_to == path
3209 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3210 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3211 auth = insert(:oauth_authorization, app: app)
3215 |> put_session(:return_to, path)
3216 |> get("/web/login", %{code: auth.token})
3218 assert conn.status == 302
3219 assert redirected_to(conn) == path
3223 describe "GET /web/login" do
3224 test "redirects to /oauth/authorize", %{conn: conn} do
3225 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3226 conn = get(conn, "/web/login", %{})
3228 assert conn.status == 302
3230 assert redirected_to(conn) ==
3231 "/oauth/authorize?response_type=code&client_id=#{app.client_id}&redirect_uri=.&scope=read+write+follow+push"
3234 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3235 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3236 auth = insert(:oauth_authorization, app: app)
3238 conn = get(conn, "/web/login", %{code: auth.token})
3240 assert conn.status == 302
3241 assert redirected_to(conn) == "/web/getting-started"
3244 test "redirects to the getting-started page when user assigned", %{conn: conn} do
3245 user = insert(:user)
3249 |> assign(:user, user)
3250 |> get("/web/login", %{})
3252 assert conn.status == 302
3253 assert redirected_to(conn) == "/web/getting-started"
3257 describe "scheduled activities" do
3258 test "creates a scheduled activity", %{conn: conn} do
3259 user = insert(:user)
3260 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3264 |> assign(:user, user)
3265 |> post("/api/v1/statuses", %{
3266 "status" => "scheduled",
3267 "scheduled_at" => scheduled_at
3270 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3271 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3272 assert [] == Repo.all(Activity)
3275 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3276 user = insert(:user)
3277 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3279 file = %Plug.Upload{
3280 content_type: "image/jpg",
3281 path: Path.absname("test/fixtures/image.jpg"),
3282 filename: "an_image.jpg"
3285 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3289 |> assign(:user, user)
3290 |> post("/api/v1/statuses", %{
3291 "media_ids" => [to_string(upload.id)],
3292 "status" => "scheduled",
3293 "scheduled_at" => scheduled_at
3296 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3297 assert %{"type" => "image"} = media_attachment
3300 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3302 user = insert(:user)
3305 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3309 |> assign(:user, user)
3310 |> post("/api/v1/statuses", %{
3311 "status" => "not scheduled",
3312 "scheduled_at" => scheduled_at
3315 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3316 assert [] == Repo.all(ScheduledActivity)
3319 test "returns error when daily user limit is exceeded", %{conn: conn} do
3320 user = insert(:user)
3323 NaiveDateTime.utc_now()
3324 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3325 |> NaiveDateTime.to_iso8601()
3327 attrs = %{params: %{}, scheduled_at: today}
3328 {:ok, _} = ScheduledActivity.create(user, attrs)
3329 {:ok, _} = ScheduledActivity.create(user, attrs)
3333 |> assign(:user, user)
3334 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3336 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3339 test "returns error when total user limit is exceeded", %{conn: conn} do
3340 user = insert(:user)
3343 NaiveDateTime.utc_now()
3344 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3345 |> NaiveDateTime.to_iso8601()
3348 NaiveDateTime.utc_now()
3349 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3350 |> NaiveDateTime.to_iso8601()
3352 attrs = %{params: %{}, scheduled_at: today}
3353 {:ok, _} = ScheduledActivity.create(user, attrs)
3354 {:ok, _} = ScheduledActivity.create(user, attrs)
3355 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3359 |> assign(:user, user)
3360 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3362 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3365 test "shows scheduled activities", %{conn: conn} do
3366 user = insert(:user)
3367 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3368 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3369 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3370 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3374 |> assign(:user, user)
3379 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3381 result = json_response(conn_res, 200)
3382 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3387 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3389 result = json_response(conn_res, 200)
3390 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3395 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3397 result = json_response(conn_res, 200)
3398 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3401 test "shows a scheduled activity", %{conn: conn} do
3402 user = insert(:user)
3403 scheduled_activity = insert(:scheduled_activity, user: user)
3407 |> assign(:user, user)
3408 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3410 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3411 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3415 |> assign(:user, user)
3416 |> get("/api/v1/scheduled_statuses/404")
3418 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3421 test "updates a scheduled activity", %{conn: conn} do
3422 user = insert(:user)
3423 scheduled_activity = insert(:scheduled_activity, user: user)
3426 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3430 |> assign(:user, user)
3431 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3432 scheduled_at: new_scheduled_at
3435 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3436 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3440 |> assign(:user, user)
3441 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3443 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3446 test "deletes a scheduled activity", %{conn: conn} do
3447 user = insert(:user)
3448 scheduled_activity = insert(:scheduled_activity, user: user)
3452 |> assign(:user, user)
3453 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3455 assert %{} = json_response(res_conn, 200)
3456 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3460 |> assign(:user, user)
3461 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3463 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3467 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3468 user1 = insert(:user)
3469 user2 = insert(:user)
3470 user3 = insert(:user)
3472 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3474 # Reply to status from another user
3477 |> assign(:user, user2)
3478 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3480 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3482 activity = Activity.get_by_id_with_object(id)
3484 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3485 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3487 # Reblog from the third user
3490 |> assign(:user, user3)
3491 |> post("/api/v1/statuses/#{activity.id}/reblog")
3493 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3494 json_response(conn2, 200)
3496 assert to_string(activity.id) == id
3498 # Getting third user status
3501 |> assign(:user, user3)
3502 |> get("api/v1/timelines/home")
3504 [reblogged_activity] = json_response(conn3, 200)
3506 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3508 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3509 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3512 describe "create account by app" do
3516 email: "lain@example.org",
3517 password: "PlzDontHackLain",
3521 [valid_params: valid_params]
3524 test "Account registration via Application", %{conn: conn} do
3527 |> post("/api/v1/apps", %{
3528 client_name: "client_name",
3529 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3530 scopes: "read, write, follow"
3534 "client_id" => client_id,
3535 "client_secret" => client_secret,
3537 "name" => "client_name",
3538 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3541 } = json_response(conn, 200)
3545 |> post("/oauth/token", %{
3546 grant_type: "client_credentials",
3547 client_id: client_id,
3548 client_secret: client_secret
3551 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3552 json_response(conn, 200)
3555 token_from_db = Repo.get_by(Token, token: token)
3556 assert token_from_db
3558 assert scope == "read write follow"
3562 |> put_req_header("authorization", "Bearer " <> token)
3563 |> post("/api/v1/accounts", %{
3565 email: "lain@example.org",
3566 password: "PlzDontHackLain",
3572 "access_token" => token,
3573 "created_at" => _created_at,
3575 "token_type" => "Bearer"
3576 } = json_response(conn, 200)
3578 token_from_db = Repo.get_by(Token, token: token)
3579 assert token_from_db
3580 token_from_db = Repo.preload(token_from_db, :user)
3581 assert token_from_db.user
3583 assert token_from_db.user.info.confirmation_pending
3586 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
3587 _user = insert(:user, email: "lain@example.org")
3588 app_token = insert(:oauth_token, user: nil)
3592 |> put_req_header("authorization", "Bearer " <> app_token.token)
3594 res = post(conn, "/api/v1/accounts", valid_params)
3595 assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
3598 test "rate limit", %{conn: conn} do
3599 app_token = insert(:oauth_token, user: nil)
3602 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3603 |> Map.put(:remote_ip, {15, 15, 15, 15})
3608 |> post("/api/v1/accounts", %{
3609 username: "#{i}lain",
3610 email: "#{i}lain@example.org",
3611 password: "PlzDontHackLain",
3616 "access_token" => token,
3617 "created_at" => _created_at,
3619 "token_type" => "Bearer"
3620 } = json_response(conn, 200)
3622 token_from_db = Repo.get_by(Token, token: token)
3623 assert token_from_db
3624 token_from_db = Repo.preload(token_from_db, :user)
3625 assert token_from_db.user
3627 assert token_from_db.user.info.confirmation_pending
3632 |> post("/api/v1/accounts", %{
3634 email: "6lain@example.org",
3635 password: "PlzDontHackLain",
3639 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3642 test "returns bad_request if missing required params", %{
3644 valid_params: valid_params
3646 app_token = insert(:oauth_token, user: nil)
3650 |> put_req_header("authorization", "Bearer " <> app_token.token)
3652 res = post(conn, "/api/v1/accounts", valid_params)
3653 assert json_response(res, 200)
3655 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
3656 |> Stream.zip(valid_params)
3657 |> Enum.each(fn {ip, {attr, _}} ->
3660 |> Map.put(:remote_ip, ip)
3661 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
3662 |> json_response(400)
3664 assert res == %{"error" => "Missing parameters"}
3668 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
3671 |> put_req_header("authorization", "Bearer " <> "invalid-token")
3673 res = post(conn, "/api/v1/accounts", valid_params)
3674 assert json_response(res, 403) == %{"error" => "Invalid credentials"}
3678 describe "GET /api/v1/polls/:id" do
3679 test "returns poll entity for object id", %{conn: conn} do
3680 user = insert(:user)
3683 CommonAPI.post(user, %{
3684 "status" => "Pleroma does",
3685 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3688 object = Object.normalize(activity)
3692 |> assign(:user, user)
3693 |> get("/api/v1/polls/#{object.id}")
3695 response = json_response(conn, 200)
3696 id = to_string(object.id)
3697 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3700 test "does not expose polls for private statuses", %{conn: conn} do
3701 user = insert(:user)
3702 other_user = insert(:user)
3705 CommonAPI.post(user, %{
3706 "status" => "Pleroma does",
3707 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3708 "visibility" => "private"
3711 object = Object.normalize(activity)
3715 |> assign(:user, other_user)
3716 |> get("/api/v1/polls/#{object.id}")
3718 assert json_response(conn, 404)
3722 describe "POST /api/v1/polls/:id/votes" do
3723 test "votes are added to the poll", %{conn: conn} do
3724 user = insert(:user)
3725 other_user = insert(:user)
3728 CommonAPI.post(user, %{
3729 "status" => "A very delicious sandwich",
3731 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3737 object = Object.normalize(activity)
3741 |> assign(:user, other_user)
3742 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3744 assert json_response(conn, 200)
3745 object = Object.get_by_id(object.id)
3747 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3752 test "author can't vote", %{conn: conn} do
3753 user = insert(:user)
3756 CommonAPI.post(user, %{
3757 "status" => "Am I cute?",
3758 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3761 object = Object.normalize(activity)
3764 |> assign(:user, user)
3765 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3766 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3768 object = Object.get_by_id(object.id)
3770 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3773 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3774 user = insert(:user)
3775 other_user = insert(:user)
3778 CommonAPI.post(user, %{
3779 "status" => "The glass is",
3780 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3783 object = Object.normalize(activity)
3786 |> assign(:user, other_user)
3787 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3788 |> json_response(422) == %{"error" => "Too many choices"}
3790 object = Object.get_by_id(object.id)
3792 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3797 test "does not allow choice index to be greater than options count", %{conn: conn} do
3798 user = insert(:user)
3799 other_user = insert(:user)
3802 CommonAPI.post(user, %{
3803 "status" => "Am I cute?",
3804 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3807 object = Object.normalize(activity)
3811 |> assign(:user, other_user)
3812 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3814 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3817 test "returns 404 error when object is not exist", %{conn: conn} do
3818 user = insert(:user)
3822 |> assign(:user, user)
3823 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3825 assert json_response(conn, 404) == %{"error" => "Record not found"}
3828 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3829 user = insert(:user)
3830 other_user = insert(:user)
3833 CommonAPI.post(user, %{
3834 "status" => "Am I cute?",
3835 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3836 "visibility" => "private"
3839 object = Object.normalize(activity)
3843 |> assign(:user, other_user)
3844 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3846 assert json_response(conn, 404) == %{"error" => "Record not found"}
3850 describe "GET /api/v1/statuses/:id/favourited_by" do
3852 user = insert(:user)
3853 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3857 |> assign(:user, user)
3859 [conn: conn, activity: activity, user: user]
3862 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3863 other_user = insert(:user)
3864 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3868 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3869 |> json_response(:ok)
3871 [%{"id" => id}] = response
3873 assert id == other_user.id
3876 test "returns empty array when status has not been favorited yet", %{
3882 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3883 |> json_response(:ok)
3885 assert Enum.empty?(response)
3888 test "does not return users who have favorited the status but are blocked", %{
3889 conn: %{assigns: %{user: user}} = conn,
3892 other_user = insert(:user)
3893 {:ok, user} = User.block(user, other_user)
3895 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3899 |> assign(:user, user)
3900 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3901 |> json_response(:ok)
3903 assert Enum.empty?(response)
3906 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3907 other_user = insert(:user)
3908 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3912 |> assign(:user, nil)
3913 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3914 |> json_response(:ok)
3916 [%{"id" => id}] = response
3917 assert id == other_user.id
3920 test "requires authentification for private posts", %{conn: conn, user: user} do
3921 other_user = insert(:user)
3924 CommonAPI.post(user, %{
3925 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3926 "visibility" => "direct"
3929 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3932 |> assign(:user, nil)
3933 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3934 |> json_response(404)
3938 |> assign(:user, other_user)
3939 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3940 |> json_response(200)
3942 [%{"id" => id}] = response
3943 assert id == other_user.id
3947 describe "GET /api/v1/statuses/:id/reblogged_by" do
3949 user = insert(:user)
3950 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3954 |> assign(:user, user)
3956 [conn: conn, activity: activity, user: user]
3959 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3960 other_user = insert(:user)
3961 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3965 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3966 |> json_response(:ok)
3968 [%{"id" => id}] = response
3970 assert id == other_user.id
3973 test "returns empty array when status has not been reblogged yet", %{
3979 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3980 |> json_response(:ok)
3982 assert Enum.empty?(response)
3985 test "does not return users who have reblogged the status but are blocked", %{
3986 conn: %{assigns: %{user: user}} = conn,
3989 other_user = insert(:user)
3990 {:ok, user} = User.block(user, other_user)
3992 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3996 |> assign(:user, user)
3997 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3998 |> json_response(:ok)
4000 assert Enum.empty?(response)
4003 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
4004 other_user = insert(:user)
4005 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
4009 |> assign(:user, nil)
4010 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4011 |> json_response(:ok)
4013 [%{"id" => id}] = response
4014 assert id == other_user.id
4017 test "requires authentification for private posts", %{conn: conn, user: user} do
4018 other_user = insert(:user)
4021 CommonAPI.post(user, %{
4022 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
4023 "visibility" => "direct"
4027 |> assign(:user, nil)
4028 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4029 |> json_response(404)
4033 |> assign(:user, other_user)
4034 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4035 |> json_response(200)
4037 assert [] == response
4041 describe "POST /auth/password, with valid parameters" do
4042 setup %{conn: conn} do
4043 user = insert(:user)
4044 conn = post(conn, "/auth/password?email=#{user.email}")
4045 %{conn: conn, user: user}
4048 test "it returns 204", %{conn: conn} do
4049 assert json_response(conn, :no_content)
4052 test "it creates a PasswordResetToken record for user", %{user: user} do
4053 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
4057 test "it sends an email to user", %{user: user} do
4058 ObanHelpers.perform_all()
4059 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
4061 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
4062 notify_email = Config.get([:instance, :notify_email])
4063 instance_name = Config.get([:instance, :name])
4066 from: {instance_name, notify_email},
4067 to: {user.name, user.email},
4068 html_body: email.html_body
4073 describe "POST /auth/password, with invalid parameters" do
4075 user = insert(:user)
4079 test "it returns 404 when user is not found", %{conn: conn, user: user} do
4080 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
4081 assert conn.status == 404
4082 assert conn.resp_body == ""
4085 test "it returns 400 when user is not local", %{conn: conn, user: user} do
4086 {:ok, user} = Repo.update(Changeset.change(user, local: false))
4087 conn = post(conn, "/auth/password?email=#{user.email}")
4088 assert conn.status == 400
4089 assert conn.resp_body == ""
4093 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
4095 user = insert(:user)
4096 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
4100 |> Changeset.change()
4101 |> Changeset.put_embed(:info, info_change)
4104 assert user.info.confirmation_pending
4109 clear_config([:instance, :account_activation_required]) do
4110 Config.put([:instance, :account_activation_required], true)
4113 test "resend account confirmation email", %{conn: conn, user: user} do
4115 |> assign(:user, user)
4116 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
4117 |> json_response(:no_content)
4119 ObanHelpers.perform_all()
4121 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
4122 notify_email = Config.get([:instance, :notify_email])
4123 instance_name = Config.get([:instance, :name])
4126 from: {instance_name, notify_email},
4127 to: {user.name, user.email},
4128 html_body: email.html_body
4133 describe "GET /api/v1/suggestions" do
4135 user = insert(:user)
4136 other_user = insert(:user)
4137 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
4138 url500 = "http://test500?#{host}&#{user.nickname}"
4139 url200 = "http://test200?#{host}&#{user.nickname}"
4142 %{method: :get, url: ^url500} ->
4143 %Tesla.Env{status: 500, body: "bad request"}
4145 %{method: :get, url: ^url200} ->
4149 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4151 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4155 [user: user, other_user: other_user]
4158 clear_config(:suggestions)
4160 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4161 Config.put([:suggestions, :enabled], false)
4165 |> assign(:user, user)
4166 |> get("/api/v1/suggestions")
4167 |> json_response(200)
4172 test "returns error", %{conn: conn, user: user} do
4173 Config.put([:suggestions, :enabled], true)
4174 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4176 assert capture_log(fn ->
4179 |> assign(:user, user)
4180 |> get("/api/v1/suggestions")
4181 |> json_response(500)
4183 assert res == "Something went wrong"
4184 end) =~ "Could not retrieve suggestions"
4187 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4188 Config.put([:suggestions, :enabled], true)
4189 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4193 |> assign(:user, user)
4194 |> get("/api/v1/suggestions")
4195 |> json_response(200)
4200 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4201 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4205 "acct" => other_user.ap_id,
4206 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4207 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4208 "id" => other_user.id
4214 describe "PUT /api/v1/media/:id" do
4216 actor = insert(:user)
4218 file = %Plug.Upload{
4219 content_type: "image/jpg",
4220 path: Path.absname("test/fixtures/image.jpg"),
4221 filename: "an_image.jpg"
4224 {:ok, %Object{} = object} =
4227 actor: User.ap_id(actor),
4228 description: "test-m"
4231 [actor: actor, object: object]
4234 test "updates name of media", %{conn: conn, actor: actor, object: object} do
4237 |> assign(:user, actor)
4238 |> put("/api/v1/media/#{object.id}", %{"description" => "test-media"})
4239 |> json_response(:ok)
4241 assert media["description"] == "test-media"
4242 assert refresh_record(object).data["name"] == "test-media"
4245 test "returns error wheb request is bad", %{conn: conn, actor: actor, object: object} do
4248 |> assign(:user, actor)
4249 |> put("/api/v1/media/#{object.id}", %{})
4250 |> json_response(400)
4252 assert media == %{"error" => "bad_request"}
4256 describe "DELETE /auth/sign_out" do
4257 test "redirect to root page", %{conn: conn} do
4258 user = insert(:user)
4262 |> assign(:user, user)
4263 |> delete("/auth/sign_out")
4265 assert conn.status == 302
4266 assert redirected_to(conn) == "/"
4270 describe "GET /api/v1/accounts/:id/lists - account_lists" do
4271 test "returns lists to which the account belongs", %{conn: conn} do
4272 user = insert(:user)
4273 other_user = insert(:user)
4274 assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user)
4275 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
4279 |> assign(:user, user)
4280 |> get("/api/v1/accounts/#{other_user.id}/lists")
4281 |> json_response(200)
4283 assert res == [%{"id" => to_string(list.id), "title" => "Test List"}]
4287 describe "empty_array, stubs for mastodon api" do
4288 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
4289 user = insert(:user)
4293 |> assign(:user, user)
4294 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
4295 |> json_response(200)
4300 test "GET /api/v1/endorsements", %{conn: conn} do
4301 user = insert(:user)
4305 |> assign(:user, user)
4306 |> get("/api/v1/endorsements")
4307 |> json_response(200)
4312 test "GET /api/v1/trends", %{conn: conn} do
4313 user = insert(:user)
4317 |> assign(:user, user)
4318 |> get("/api/v1/trends")
4319 |> json_response(200)