1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.ActivityExpiration
12 alias Pleroma.Notification
15 alias Pleroma.ScheduledActivity
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.MastodonAPI.FilterView
20 alias Pleroma.Web.OAuth.App
21 alias Pleroma.Web.OAuth.Token
22 alias Pleroma.Web.OStatus
23 alias Pleroma.Web.Push
24 import Pleroma.Factory
25 import ExUnit.CaptureLog
27 import Swoosh.TestAssertions
29 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
32 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
36 clear_config([:instance, :public])
37 clear_config([:rich_media, :enabled])
39 test "the home timeline", %{conn: conn} do
41 following = insert(:user)
43 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
47 |> assign(:user, user)
48 |> get("/api/v1/timelines/home")
50 assert Enum.empty?(json_response(conn, 200))
52 {:ok, user} = User.follow(user, following)
56 |> assign(:user, user)
57 |> get("/api/v1/timelines/home")
59 assert [%{"content" => "test"}] = json_response(conn, 200)
62 test "the public timeline", %{conn: conn} do
63 following = insert(:user)
66 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
69 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
73 |> get("/api/v1/timelines/public", %{"local" => "False"})
75 assert length(json_response(conn, 200)) == 2
79 |> get("/api/v1/timelines/public", %{"local" => "True"})
81 assert [%{"content" => "test"}] = json_response(conn, 200)
85 |> get("/api/v1/timelines/public", %{"local" => "1"})
87 assert [%{"content" => "test"}] = json_response(conn, 200)
91 test "the public timeline when public is set to false", %{conn: conn} do
92 Config.put([:instance, :public], false)
95 |> get("/api/v1/timelines/public", %{"local" => "False"})
96 |> json_response(403) == %{"error" => "This resource requires authentication."}
99 describe "posting statuses" do
105 |> assign(:user, user)
110 test "posting a status", %{conn: conn} do
111 idempotency_key = "Pikachu rocks!"
115 |> put_req_header("idempotency-key", idempotency_key)
116 |> post("/api/v1/statuses", %{
118 "spoiler_text" => "2hu",
119 "sensitive" => "false"
122 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
124 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
126 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
127 json_response(conn_one, 200)
129 assert Activity.get_by_id(id)
133 |> put_req_header("idempotency-key", idempotency_key)
134 |> post("/api/v1/statuses", %{
136 "spoiler_text" => "2hu",
137 "sensitive" => "false"
140 assert %{"id" => second_id} = json_response(conn_two, 200)
141 assert id == second_id
145 |> post("/api/v1/statuses", %{
147 "spoiler_text" => "2hu",
148 "sensitive" => "false"
151 assert %{"id" => third_id} = json_response(conn_three, 200)
152 refute id == third_id
154 # An activity that will expire:
156 expires_in = 120 * 60
160 |> post("api/v1/statuses", %{
161 "status" => "oolong",
162 "expires_in" => expires_in
165 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
166 assert activity = Activity.get_by_id(fourth_id)
167 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
169 estimated_expires_at =
170 NaiveDateTime.utc_now()
171 |> NaiveDateTime.add(expires_in)
172 |> NaiveDateTime.truncate(:second)
174 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
175 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
177 assert fourth_response["pleroma"]["expires_at"] ==
178 NaiveDateTime.to_iso8601(expiration.scheduled_at)
181 test "replying to a status", %{conn: conn} do
183 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
187 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
189 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
191 activity = Activity.get_by_id(id)
193 assert activity.data["context"] == replied_to.data["context"]
194 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
197 test "replying to a direct message with visibility other than direct", %{conn: conn} do
199 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
201 Enum.each(["public", "private", "unlisted"], fn visibility ->
204 |> post("/api/v1/statuses", %{
205 "status" => "@#{user.nickname} hey",
206 "in_reply_to_id" => replied_to.id,
207 "visibility" => visibility
210 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
214 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
217 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
219 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
220 assert Activity.get_by_id(id)
223 test "posting a sensitive status", %{conn: conn} do
226 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
228 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
229 assert Activity.get_by_id(id)
232 test "posting a fake status", %{conn: conn} do
235 |> post("/api/v1/statuses", %{
237 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
240 real_status = json_response(real_conn, 200)
243 assert Object.get_by_ap_id(real_status["uri"])
247 |> Map.put("id", nil)
248 |> Map.put("url", nil)
249 |> Map.put("uri", nil)
250 |> Map.put("created_at", nil)
251 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
255 |> post("/api/v1/statuses", %{
257 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
261 fake_status = json_response(fake_conn, 200)
264 refute Object.get_by_ap_id(fake_status["uri"])
268 |> Map.put("id", nil)
269 |> Map.put("url", nil)
270 |> Map.put("uri", nil)
271 |> Map.put("created_at", nil)
272 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
274 assert real_status == fake_status
277 test "posting a status with OGP link preview", %{conn: conn} do
278 Config.put([:rich_media, :enabled], true)
282 |> post("/api/v1/statuses", %{
283 "status" => "https://example.com/ogp"
286 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
287 assert Activity.get_by_id(id)
290 test "posting a direct status", %{conn: conn} do
291 user2 = insert(:user)
292 content = "direct cofe @#{user2.nickname}"
296 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
298 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
299 assert activity = Activity.get_by_id(id)
300 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
301 assert activity.data["to"] == [user2.ap_id]
302 assert activity.data["cc"] == []
306 describe "posting polls" do
307 test "posting a poll", %{conn: conn} do
309 time = NaiveDateTime.utc_now()
313 |> assign(:user, user)
314 |> post("/api/v1/statuses", %{
315 "status" => "Who is the #bestgrill?",
316 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
319 response = json_response(conn, 200)
321 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
322 title in ["Rei", "Asuka", "Misato"]
325 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
326 refute response["poll"]["expred"]
329 test "option limit is enforced", %{conn: conn} do
331 limit = Config.get([:instance, :poll_limits, :max_options])
335 |> assign(:user, user)
336 |> post("/api/v1/statuses", %{
338 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
341 %{"error" => error} = json_response(conn, 422)
342 assert error == "Poll can't contain more than #{limit} options"
345 test "option character limit is enforced", %{conn: conn} do
347 limit = Config.get([:instance, :poll_limits, :max_option_chars])
351 |> assign(:user, user)
352 |> post("/api/v1/statuses", %{
355 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
360 %{"error" => error} = json_response(conn, 422)
361 assert error == "Poll options cannot be longer than #{limit} characters each"
364 test "minimal date limit is enforced", %{conn: conn} do
366 limit = Config.get([:instance, :poll_limits, :min_expiration])
370 |> assign(:user, user)
371 |> post("/api/v1/statuses", %{
372 "status" => "imagine arbitrary limits",
374 "options" => ["this post was made by pleroma gang"],
375 "expires_in" => limit - 1
379 %{"error" => error} = json_response(conn, 422)
380 assert error == "Expiration date is too soon"
383 test "maximum date limit is enforced", %{conn: conn} do
385 limit = Config.get([:instance, :poll_limits, :max_expiration])
389 |> assign(:user, user)
390 |> post("/api/v1/statuses", %{
391 "status" => "imagine arbitrary limits",
393 "options" => ["this post was made by pleroma gang"],
394 "expires_in" => limit + 1
398 %{"error" => error} = json_response(conn, 422)
399 assert error == "Expiration date is too far in the future"
403 test "direct timeline", %{conn: conn} do
404 user_one = insert(:user)
405 user_two = insert(:user)
407 {:ok, user_two} = User.follow(user_two, user_one)
410 CommonAPI.post(user_one, %{
411 "status" => "Hi @#{user_two.nickname}!",
412 "visibility" => "direct"
415 {:ok, _follower_only} =
416 CommonAPI.post(user_one, %{
417 "status" => "Hi @#{user_two.nickname}!",
418 "visibility" => "private"
421 # Only direct should be visible here
424 |> assign(:user, user_two)
425 |> get("api/v1/timelines/direct")
427 [status] = json_response(res_conn, 200)
429 assert %{"visibility" => "direct"} = status
430 assert status["url"] != direct.data["id"]
432 # User should be able to see their own direct message
435 |> assign(:user, user_one)
436 |> get("api/v1/timelines/direct")
438 [status] = json_response(res_conn, 200)
440 assert %{"visibility" => "direct"} = status
442 # Both should be visible here
445 |> assign(:user, user_two)
446 |> get("api/v1/timelines/home")
448 [_s1, _s2] = json_response(res_conn, 200)
451 Enum.each(1..20, fn _ ->
453 CommonAPI.post(user_one, %{
454 "status" => "Hi @#{user_two.nickname}!",
455 "visibility" => "direct"
461 |> assign(:user, user_two)
462 |> get("api/v1/timelines/direct")
464 statuses = json_response(res_conn, 200)
465 assert length(statuses) == 20
469 |> assign(:user, user_two)
470 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
472 [status] = json_response(res_conn, 200)
474 assert status["url"] != direct.data["id"]
477 test "Conversations", %{conn: conn} do
478 user_one = insert(:user)
479 user_two = insert(:user)
480 user_three = insert(:user)
482 {:ok, user_two} = User.follow(user_two, user_one)
485 CommonAPI.post(user_one, %{
486 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
487 "visibility" => "direct"
490 {:ok, _follower_only} =
491 CommonAPI.post(user_one, %{
492 "status" => "Hi @#{user_two.nickname}!",
493 "visibility" => "private"
498 |> assign(:user, user_one)
499 |> get("/api/v1/conversations")
501 assert response = json_response(res_conn, 200)
506 "accounts" => res_accounts,
507 "last_status" => res_last_status,
512 account_ids = Enum.map(res_accounts, & &1["id"])
513 assert length(res_accounts) == 2
514 assert user_two.id in account_ids
515 assert user_three.id in account_ids
516 assert is_binary(res_id)
517 assert unread == true
518 assert res_last_status["id"] == direct.id
520 # Apparently undocumented API endpoint
523 |> assign(:user, user_one)
524 |> post("/api/v1/conversations/#{res_id}/read")
526 assert response = json_response(res_conn, 200)
527 assert length(response["accounts"]) == 2
528 assert response["last_status"]["id"] == direct.id
529 assert response["unread"] == false
531 # (vanilla) Mastodon frontend behaviour
534 |> assign(:user, user_one)
535 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
537 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
540 test "doesn't include DMs from blocked users", %{conn: conn} do
541 blocker = insert(:user)
542 blocked = insert(:user)
544 {:ok, blocker} = User.block(blocker, blocked)
546 {:ok, _blocked_direct} =
547 CommonAPI.post(blocked, %{
548 "status" => "Hi @#{blocker.nickname}!",
549 "visibility" => "direct"
553 CommonAPI.post(user, %{
554 "status" => "Hi @#{blocker.nickname}!",
555 "visibility" => "direct"
560 |> assign(:user, user)
561 |> get("api/v1/timelines/direct")
563 [status] = json_response(res_conn, 200)
564 assert status["id"] == direct.id
567 test "verify_credentials", %{conn: conn} do
572 |> assign(:user, user)
573 |> get("/api/v1/accounts/verify_credentials")
575 response = json_response(conn, 200)
577 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
578 assert response["pleroma"]["chat_token"]
579 assert id == to_string(user.id)
582 test "verify_credentials default scope unlisted", %{conn: conn} do
583 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
587 |> assign(:user, user)
588 |> get("/api/v1/accounts/verify_credentials")
590 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
591 assert id == to_string(user.id)
594 test "apps/verify_credentials", %{conn: conn} do
595 token = insert(:oauth_token)
599 |> assign(:user, token.user)
600 |> assign(:token, token)
601 |> get("/api/v1/apps/verify_credentials")
603 app = Repo.preload(token, :app).app
606 "name" => app.client_name,
607 "website" => app.website,
608 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
611 assert expected == json_response(conn, 200)
614 test "user avatar can be set", %{conn: conn} do
616 avatar_image = File.read!("test/fixtures/avatar_data_uri")
620 |> assign(:user, user)
621 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
623 user = refresh_record(user)
637 assert %{"url" => _} = json_response(conn, 200)
640 test "user avatar can be reset", %{conn: conn} do
645 |> assign(:user, user)
646 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
648 user = User.get_cached_by_id(user.id)
650 assert user.avatar == nil
652 assert %{"url" => nil} = json_response(conn, 200)
655 test "can set profile banner", %{conn: conn} do
660 |> assign(:user, user)
661 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
663 user = refresh_record(user)
664 assert user.info.banner["type"] == "Image"
666 assert %{"url" => _} = json_response(conn, 200)
669 test "can reset profile banner", %{conn: conn} do
674 |> assign(:user, user)
675 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
677 user = refresh_record(user)
678 assert user.info.banner == %{}
680 assert %{"url" => nil} = json_response(conn, 200)
683 test "background image can be set", %{conn: conn} do
688 |> assign(:user, user)
689 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
691 user = refresh_record(user)
692 assert user.info.background["type"] == "Image"
693 assert %{"url" => _} = json_response(conn, 200)
696 test "background image can be reset", %{conn: conn} do
701 |> assign(:user, user)
702 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
704 user = refresh_record(user)
705 assert user.info.background == %{}
706 assert %{"url" => nil} = json_response(conn, 200)
709 test "creates an oauth app", %{conn: conn} do
711 app_attrs = build(:oauth_app)
715 |> assign(:user, user)
716 |> post("/api/v1/apps", %{
717 client_name: app_attrs.client_name,
718 redirect_uris: app_attrs.redirect_uris
721 [app] = Repo.all(App)
724 "name" => app.client_name,
725 "website" => app.website,
726 "client_id" => app.client_id,
727 "client_secret" => app.client_secret,
728 "id" => app.id |> to_string(),
729 "redirect_uri" => app.redirect_uris,
730 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
733 assert expected == json_response(conn, 200)
736 test "get a status", %{conn: conn} do
737 activity = insert(:note_activity)
741 |> get("/api/v1/statuses/#{activity.id}")
743 assert %{"id" => id} = json_response(conn, 200)
744 assert id == to_string(activity.id)
747 describe "deleting a status" do
748 test "when you created it", %{conn: conn} do
749 activity = insert(:note_activity)
750 author = User.get_cached_by_ap_id(activity.data["actor"])
754 |> assign(:user, author)
755 |> delete("/api/v1/statuses/#{activity.id}")
757 assert %{} = json_response(conn, 200)
759 refute Activity.get_by_id(activity.id)
762 test "when you didn't create it", %{conn: conn} do
763 activity = insert(:note_activity)
768 |> assign(:user, user)
769 |> delete("/api/v1/statuses/#{activity.id}")
771 assert %{"error" => _} = json_response(conn, 403)
773 assert Activity.get_by_id(activity.id) == activity
776 test "when you're an admin or moderator", %{conn: conn} do
777 activity1 = insert(:note_activity)
778 activity2 = insert(:note_activity)
779 admin = insert(:user, info: %{is_admin: true})
780 moderator = insert(:user, info: %{is_moderator: true})
784 |> assign(:user, admin)
785 |> delete("/api/v1/statuses/#{activity1.id}")
787 assert %{} = json_response(res_conn, 200)
791 |> assign(:user, moderator)
792 |> delete("/api/v1/statuses/#{activity2.id}")
794 assert %{} = json_response(res_conn, 200)
796 refute Activity.get_by_id(activity1.id)
797 refute Activity.get_by_id(activity2.id)
801 describe "filters" do
802 test "creating a filter", %{conn: conn} do
805 filter = %Pleroma.Filter{
812 |> assign(:user, user)
813 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
815 assert response = json_response(conn, 200)
816 assert response["phrase"] == filter.phrase
817 assert response["context"] == filter.context
818 assert response["irreversible"] == false
819 assert response["id"] != nil
820 assert response["id"] != ""
823 test "fetching a list of filters", %{conn: conn} do
826 query_one = %Pleroma.Filter{
833 query_two = %Pleroma.Filter{
840 {:ok, filter_one} = Pleroma.Filter.create(query_one)
841 {:ok, filter_two} = Pleroma.Filter.create(query_two)
845 |> assign(:user, user)
846 |> get("/api/v1/filters")
847 |> json_response(200)
853 filters: [filter_two, filter_one]
857 test "get a filter", %{conn: conn} do
860 query = %Pleroma.Filter{
867 {:ok, filter} = Pleroma.Filter.create(query)
871 |> assign(:user, user)
872 |> get("/api/v1/filters/#{filter.filter_id}")
874 assert _response = json_response(conn, 200)
877 test "update a filter", %{conn: conn} do
880 query = %Pleroma.Filter{
887 {:ok, _filter} = Pleroma.Filter.create(query)
889 new = %Pleroma.Filter{
896 |> assign(:user, user)
897 |> put("/api/v1/filters/#{query.filter_id}", %{
902 assert response = json_response(conn, 200)
903 assert response["phrase"] == new.phrase
904 assert response["context"] == new.context
907 test "delete a filter", %{conn: conn} do
910 query = %Pleroma.Filter{
917 {:ok, filter} = Pleroma.Filter.create(query)
921 |> assign(:user, user)
922 |> delete("/api/v1/filters/#{filter.filter_id}")
924 assert response = json_response(conn, 200)
925 assert response == %{}
929 describe "list timelines" do
930 test "list timeline", %{conn: conn} do
932 other_user = insert(:user)
933 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
934 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
935 {:ok, list} = Pleroma.List.create("name", user)
936 {:ok, list} = Pleroma.List.follow(list, other_user)
940 |> assign(:user, user)
941 |> get("/api/v1/timelines/list/#{list.id}")
943 assert [%{"id" => id}] = json_response(conn, 200)
945 assert id == to_string(activity_two.id)
948 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
950 other_user = insert(:user)
951 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
953 {:ok, _activity_two} =
954 CommonAPI.post(other_user, %{
955 "status" => "Marisa is cute.",
956 "visibility" => "private"
959 {:ok, list} = Pleroma.List.create("name", user)
960 {:ok, list} = Pleroma.List.follow(list, other_user)
964 |> assign(:user, user)
965 |> get("/api/v1/timelines/list/#{list.id}")
967 assert [%{"id" => id}] = json_response(conn, 200)
969 assert id == to_string(activity_one.id)
973 describe "notifications" do
974 test "list of notifications", %{conn: conn} do
976 other_user = insert(:user)
978 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
980 {:ok, [_notification]} = Notification.create_notifications(activity)
984 |> assign(:user, user)
985 |> get("/api/v1/notifications")
988 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
990 }\">@<span>#{user.nickname}</span></a></span>"
992 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
993 assert response == expected_response
996 test "getting a single notification", %{conn: conn} do
998 other_user = insert(:user)
1000 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1002 {:ok, [notification]} = Notification.create_notifications(activity)
1006 |> assign(:user, user)
1007 |> get("/api/v1/notifications/#{notification.id}")
1010 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1012 }\">@<span>#{user.nickname}</span></a></span>"
1014 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1015 assert response == expected_response
1018 test "dismissing a single notification", %{conn: conn} do
1019 user = insert(:user)
1020 other_user = insert(:user)
1022 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1024 {:ok, [notification]} = Notification.create_notifications(activity)
1028 |> assign(:user, user)
1029 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1031 assert %{} = json_response(conn, 200)
1034 test "clearing all notifications", %{conn: conn} do
1035 user = insert(:user)
1036 other_user = insert(:user)
1038 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1040 {:ok, [_notification]} = Notification.create_notifications(activity)
1044 |> assign(:user, user)
1045 |> post("/api/v1/notifications/clear")
1047 assert %{} = json_response(conn, 200)
1051 |> assign(:user, user)
1052 |> get("/api/v1/notifications")
1054 assert all = json_response(conn, 200)
1058 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1059 user = insert(:user)
1060 other_user = insert(:user)
1062 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1063 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1064 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1065 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1067 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1068 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1069 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1070 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1074 |> assign(:user, user)
1079 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1081 result = json_response(conn_res, 200)
1082 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1087 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1089 result = json_response(conn_res, 200)
1090 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1095 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1097 result = json_response(conn_res, 200)
1098 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1101 test "filters notifications using exclude_types", %{conn: conn} do
1102 user = insert(:user)
1103 other_user = insert(:user)
1105 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1106 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1107 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1108 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1109 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1111 mention_notification_id =
1112 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1114 favorite_notification_id =
1115 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1117 reblog_notification_id =
1118 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1120 follow_notification_id =
1121 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1125 |> assign(:user, user)
1128 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1130 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1133 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1135 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1138 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1140 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1143 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1145 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1148 test "destroy multiple", %{conn: conn} do
1149 user = insert(:user)
1150 other_user = insert(:user)
1152 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1153 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1154 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1155 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1157 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1158 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1159 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1160 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1164 |> assign(:user, user)
1168 |> get("/api/v1/notifications")
1170 result = json_response(conn_res, 200)
1171 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1175 |> assign(:user, other_user)
1179 |> get("/api/v1/notifications")
1181 result = json_response(conn_res, 200)
1182 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1186 |> delete("/api/v1/notifications/destroy_multiple", %{
1187 "ids" => [notification1_id, notification2_id]
1190 assert json_response(conn_destroy, 200) == %{}
1194 |> get("/api/v1/notifications")
1196 result = json_response(conn_res, 200)
1197 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1200 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1201 user = insert(:user)
1202 user2 = insert(:user)
1204 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1205 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1207 conn = assign(conn, :user, user)
1209 conn = get(conn, "/api/v1/notifications")
1211 assert length(json_response(conn, 200)) == 1
1213 {:ok, user} = User.mute(user, user2)
1215 conn = assign(build_conn(), :user, user)
1216 conn = get(conn, "/api/v1/notifications")
1218 assert json_response(conn, 200) == []
1221 test "see notifications after muting user without notifications", %{conn: conn} do
1222 user = insert(:user)
1223 user2 = insert(:user)
1225 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1226 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1228 conn = assign(conn, :user, user)
1230 conn = get(conn, "/api/v1/notifications")
1232 assert length(json_response(conn, 200)) == 1
1234 {:ok, user} = User.mute(user, user2, false)
1236 conn = assign(build_conn(), :user, user)
1237 conn = get(conn, "/api/v1/notifications")
1239 assert length(json_response(conn, 200)) == 1
1242 test "see notifications after muting user with notifications and with_muted parameter", %{
1245 user = insert(:user)
1246 user2 = insert(:user)
1248 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1249 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1251 conn = assign(conn, :user, user)
1253 conn = get(conn, "/api/v1/notifications")
1255 assert length(json_response(conn, 200)) == 1
1257 {:ok, user} = User.mute(user, user2)
1259 conn = assign(build_conn(), :user, user)
1260 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1262 assert length(json_response(conn, 200)) == 1
1266 describe "reblogging" do
1267 test "reblogs and returns the reblogged status", %{conn: conn} do
1268 activity = insert(:note_activity)
1269 user = insert(:user)
1273 |> assign(:user, user)
1274 |> post("/api/v1/statuses/#{activity.id}/reblog")
1277 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1279 } = json_response(conn, 200)
1281 assert to_string(activity.id) == id
1284 test "reblogged status for another user", %{conn: conn} do
1285 activity = insert(:note_activity)
1286 user1 = insert(:user)
1287 user2 = insert(:user)
1288 user3 = insert(:user)
1289 CommonAPI.favorite(activity.id, user2)
1290 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1291 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1292 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1296 |> assign(:user, user3)
1297 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1300 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1301 "reblogged" => false,
1302 "favourited" => false,
1303 "bookmarked" => false
1304 } = json_response(conn_res, 200)
1308 |> assign(:user, user2)
1309 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1312 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1313 "reblogged" => true,
1314 "favourited" => true,
1315 "bookmarked" => true
1316 } = json_response(conn_res, 200)
1318 assert to_string(activity.id) == id
1321 test "returns 400 error when activity is not exist", %{conn: conn} do
1322 user = insert(:user)
1326 |> assign(:user, user)
1327 |> post("/api/v1/statuses/foo/reblog")
1329 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1333 describe "unreblogging" do
1334 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1335 activity = insert(:note_activity)
1336 user = insert(:user)
1338 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1342 |> assign(:user, user)
1343 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1345 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1347 assert to_string(activity.id) == id
1350 test "returns 400 error when activity is not exist", %{conn: conn} do
1351 user = insert(:user)
1355 |> assign(:user, user)
1356 |> post("/api/v1/statuses/foo/unreblog")
1358 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1362 describe "favoriting" do
1363 test "favs a status and returns it", %{conn: conn} do
1364 activity = insert(:note_activity)
1365 user = insert(:user)
1369 |> assign(:user, user)
1370 |> post("/api/v1/statuses/#{activity.id}/favourite")
1372 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1373 json_response(conn, 200)
1375 assert to_string(activity.id) == id
1378 test "returns 400 error for a wrong id", %{conn: conn} do
1379 user = insert(:user)
1383 |> assign(:user, user)
1384 |> post("/api/v1/statuses/1/favourite")
1386 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1390 describe "unfavoriting" do
1391 test "unfavorites a status and returns it", %{conn: conn} do
1392 activity = insert(:note_activity)
1393 user = insert(:user)
1395 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1399 |> assign(:user, user)
1400 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1402 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1403 json_response(conn, 200)
1405 assert to_string(activity.id) == id
1408 test "returns 400 error for a wrong id", %{conn: conn} do
1409 user = insert(:user)
1413 |> assign(:user, user)
1414 |> post("/api/v1/statuses/1/unfavourite")
1416 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1420 describe "user timelines" do
1421 test "gets a users statuses", %{conn: conn} do
1422 user_one = insert(:user)
1423 user_two = insert(:user)
1424 user_three = insert(:user)
1426 {:ok, user_three} = User.follow(user_three, user_one)
1428 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1430 {:ok, direct_activity} =
1431 CommonAPI.post(user_one, %{
1432 "status" => "Hi, @#{user_two.nickname}.",
1433 "visibility" => "direct"
1436 {:ok, private_activity} =
1437 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1441 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1443 assert [%{"id" => id}] = json_response(resp, 200)
1444 assert id == to_string(activity.id)
1448 |> assign(:user, user_two)
1449 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1451 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1452 assert id_one == to_string(direct_activity.id)
1453 assert id_two == to_string(activity.id)
1457 |> assign(:user, user_three)
1458 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1460 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1461 assert id_one == to_string(private_activity.id)
1462 assert id_two == to_string(activity.id)
1465 test "unimplemented pinned statuses feature", %{conn: conn} do
1466 note = insert(:note_activity)
1467 user = User.get_cached_by_ap_id(note.data["actor"])
1471 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1473 assert json_response(conn, 200) == []
1476 test "gets an users media", %{conn: conn} do
1477 note = insert(:note_activity)
1478 user = User.get_cached_by_ap_id(note.data["actor"])
1480 file = %Plug.Upload{
1481 content_type: "image/jpg",
1482 path: Path.absname("test/fixtures/image.jpg"),
1483 filename: "an_image.jpg"
1486 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1488 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1492 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1494 assert [%{"id" => id}] = json_response(conn, 200)
1495 assert id == to_string(image_post.id)
1499 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1501 assert [%{"id" => id}] = json_response(conn, 200)
1502 assert id == to_string(image_post.id)
1505 test "gets a user's statuses without reblogs", %{conn: conn} do
1506 user = insert(:user)
1507 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1508 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1512 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1514 assert [%{"id" => id}] = json_response(conn, 200)
1515 assert id == to_string(post.id)
1519 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1521 assert [%{"id" => id}] = json_response(conn, 200)
1522 assert id == to_string(post.id)
1525 test "filters user's statuses by a hashtag", %{conn: conn} do
1526 user = insert(:user)
1527 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1528 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1532 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1534 assert [%{"id" => id}] = json_response(conn, 200)
1535 assert id == to_string(post.id)
1539 describe "user relationships" do
1540 test "returns the relationships for the current user", %{conn: conn} do
1541 user = insert(:user)
1542 other_user = insert(:user)
1543 {:ok, user} = User.follow(user, other_user)
1547 |> assign(:user, user)
1548 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1550 assert [relationship] = json_response(conn, 200)
1552 assert to_string(other_user.id) == relationship["id"]
1555 test "returns an empty list on a bad request", %{conn: conn} do
1556 user = insert(:user)
1560 |> assign(:user, user)
1561 |> get("/api/v1/accounts/relationships", %{})
1563 assert [] = json_response(conn, 200)
1567 describe "media upload" do
1569 user = insert(:user)
1573 |> assign(:user, user)
1575 image = %Plug.Upload{
1576 content_type: "image/jpg",
1577 path: Path.absname("test/fixtures/image.jpg"),
1578 filename: "an_image.jpg"
1581 [conn: conn, image: image]
1584 clear_config([:media_proxy])
1585 clear_config([Pleroma.Upload])
1587 test "returns uploaded image", %{conn: conn, image: image} do
1588 desc = "Description of the image"
1592 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1593 |> json_response(:ok)
1595 assert media["type"] == "image"
1596 assert media["description"] == desc
1599 object = Repo.get(Object, media["id"])
1600 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1604 describe "locked accounts" do
1605 test "/api/v1/follow_requests works" do
1606 user = insert(:user, %{info: %User.Info{locked: true}})
1607 other_user = insert(:user)
1609 {:ok, _activity} = ActivityPub.follow(other_user, user)
1611 user = User.get_cached_by_id(user.id)
1612 other_user = User.get_cached_by_id(other_user.id)
1614 assert User.following?(other_user, user) == false
1618 |> assign(:user, user)
1619 |> get("/api/v1/follow_requests")
1621 assert [relationship] = json_response(conn, 200)
1622 assert to_string(other_user.id) == relationship["id"]
1625 test "/api/v1/follow_requests/:id/authorize works" do
1626 user = insert(:user, %{info: %User.Info{locked: true}})
1627 other_user = insert(:user)
1629 {:ok, _activity} = ActivityPub.follow(other_user, user)
1631 user = User.get_cached_by_id(user.id)
1632 other_user = User.get_cached_by_id(other_user.id)
1634 assert User.following?(other_user, user) == false
1638 |> assign(:user, user)
1639 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1641 assert relationship = json_response(conn, 200)
1642 assert to_string(other_user.id) == relationship["id"]
1644 user = User.get_cached_by_id(user.id)
1645 other_user = User.get_cached_by_id(other_user.id)
1647 assert User.following?(other_user, user) == true
1650 test "verify_credentials", %{conn: conn} do
1651 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1655 |> assign(:user, user)
1656 |> get("/api/v1/accounts/verify_credentials")
1658 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1659 assert id == to_string(user.id)
1662 test "/api/v1/follow_requests/:id/reject works" do
1663 user = insert(:user, %{info: %User.Info{locked: true}})
1664 other_user = insert(:user)
1666 {:ok, _activity} = ActivityPub.follow(other_user, user)
1668 user = User.get_cached_by_id(user.id)
1672 |> assign(:user, user)
1673 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1675 assert relationship = json_response(conn, 200)
1676 assert to_string(other_user.id) == relationship["id"]
1678 user = User.get_cached_by_id(user.id)
1679 other_user = User.get_cached_by_id(other_user.id)
1681 assert User.following?(other_user, user) == false
1685 describe "account fetching" do
1686 test "works by id" do
1687 user = insert(:user)
1691 |> get("/api/v1/accounts/#{user.id}")
1693 assert %{"id" => id} = json_response(conn, 200)
1694 assert id == to_string(user.id)
1698 |> get("/api/v1/accounts/-1")
1700 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1703 test "works by nickname" do
1704 user = insert(:user)
1708 |> get("/api/v1/accounts/#{user.nickname}")
1710 assert %{"id" => id} = json_response(conn, 200)
1711 assert id == user.id
1714 test "works by nickname for remote users" do
1715 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1716 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1717 user = insert(:user, nickname: "user@example.com", local: false)
1721 |> get("/api/v1/accounts/#{user.nickname}")
1723 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1724 assert %{"id" => id} = json_response(conn, 200)
1725 assert id == user.id
1728 test "respects limit_to_local_content == :all for remote user nicknames" do
1729 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1730 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1732 user = insert(:user, nickname: "user@example.com", local: false)
1736 |> get("/api/v1/accounts/#{user.nickname}")
1738 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1739 assert json_response(conn, 404)
1742 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1743 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1744 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1746 user = insert(:user, nickname: "user@example.com", local: false)
1747 reading_user = insert(:user)
1751 |> get("/api/v1/accounts/#{user.nickname}")
1753 assert json_response(conn, 404)
1757 |> assign(:user, reading_user)
1758 |> get("/api/v1/accounts/#{user.nickname}")
1760 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1761 assert %{"id" => id} = json_response(conn, 200)
1762 assert id == user.id
1766 describe "/api/v1/pleroma/mascot" do
1767 test "mascot upload", %{conn: conn} do
1768 user = insert(:user)
1770 non_image_file = %Plug.Upload{
1771 content_type: "audio/mpeg",
1772 path: Path.absname("test/fixtures/sound.mp3"),
1773 filename: "sound.mp3"
1778 |> assign(:user, user)
1779 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1781 assert json_response(conn, 415)
1783 file = %Plug.Upload{
1784 content_type: "image/jpg",
1785 path: Path.absname("test/fixtures/image.jpg"),
1786 filename: "an_image.jpg"
1791 |> assign(:user, user)
1792 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1794 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1797 test "mascot retrieving", %{conn: conn} do
1798 user = insert(:user)
1799 # When user hasn't set a mascot, we should just get pleroma tan back
1802 |> assign(:user, user)
1803 |> get("/api/v1/pleroma/mascot")
1805 assert %{"url" => url} = json_response(conn, 200)
1806 assert url =~ "pleroma-fox-tan-smol"
1808 # When a user sets their mascot, we should get that back
1809 file = %Plug.Upload{
1810 content_type: "image/jpg",
1811 path: Path.absname("test/fixtures/image.jpg"),
1812 filename: "an_image.jpg"
1817 |> assign(:user, user)
1818 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1820 assert json_response(conn, 200)
1822 user = User.get_cached_by_id(user.id)
1826 |> assign(:user, user)
1827 |> get("/api/v1/pleroma/mascot")
1829 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1830 assert url =~ "an_image"
1834 test "hashtag timeline", %{conn: conn} do
1835 following = insert(:user)
1838 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1840 {:ok, [_activity]} =
1841 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1845 |> get("/api/v1/timelines/tag/2hu")
1847 assert [%{"id" => id}] = json_response(nconn, 200)
1849 assert id == to_string(activity.id)
1851 # works for different capitalization too
1854 |> get("/api/v1/timelines/tag/2HU")
1856 assert [%{"id" => id}] = json_response(nconn, 200)
1858 assert id == to_string(activity.id)
1862 test "multi-hashtag timeline", %{conn: conn} do
1863 user = insert(:user)
1865 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1866 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1867 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1871 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1873 [status_none, status_test1, status_test] = json_response(any_test, 200)
1875 assert to_string(activity_test.id) == status_test["id"]
1876 assert to_string(activity_test1.id) == status_test1["id"]
1877 assert to_string(activity_none.id) == status_none["id"]
1881 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1883 assert [status_test1] == json_response(restricted_test, 200)
1885 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1887 assert [status_none] == json_response(all_test, 200)
1890 test "getting followers", %{conn: conn} do
1891 user = insert(:user)
1892 other_user = insert(:user)
1893 {:ok, user} = User.follow(user, other_user)
1897 |> get("/api/v1/accounts/#{other_user.id}/followers")
1899 assert [%{"id" => id}] = json_response(conn, 200)
1900 assert id == to_string(user.id)
1903 test "getting followers, hide_followers", %{conn: conn} do
1904 user = insert(:user)
1905 other_user = insert(:user, %{info: %{hide_followers: true}})
1906 {:ok, _user} = User.follow(user, other_user)
1910 |> get("/api/v1/accounts/#{other_user.id}/followers")
1912 assert [] == json_response(conn, 200)
1915 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1916 user = insert(:user)
1917 other_user = insert(:user, %{info: %{hide_followers: true}})
1918 {:ok, _user} = User.follow(user, other_user)
1922 |> assign(:user, other_user)
1923 |> get("/api/v1/accounts/#{other_user.id}/followers")
1925 refute [] == json_response(conn, 200)
1928 test "getting followers, pagination", %{conn: conn} do
1929 user = insert(:user)
1930 follower1 = insert(:user)
1931 follower2 = insert(:user)
1932 follower3 = insert(:user)
1933 {:ok, _} = User.follow(follower1, user)
1934 {:ok, _} = User.follow(follower2, user)
1935 {:ok, _} = User.follow(follower3, user)
1939 |> assign(:user, user)
1943 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1945 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1946 assert id3 == follower3.id
1947 assert id2 == follower2.id
1951 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1953 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1954 assert id2 == follower2.id
1955 assert id1 == follower1.id
1959 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1961 assert [%{"id" => id2}] = json_response(res_conn, 200)
1962 assert id2 == follower2.id
1964 assert [link_header] = get_resp_header(res_conn, "link")
1965 assert link_header =~ ~r/min_id=#{follower2.id}/
1966 assert link_header =~ ~r/max_id=#{follower2.id}/
1969 test "getting following", %{conn: conn} do
1970 user = insert(:user)
1971 other_user = insert(:user)
1972 {:ok, user} = User.follow(user, other_user)
1976 |> get("/api/v1/accounts/#{user.id}/following")
1978 assert [%{"id" => id}] = json_response(conn, 200)
1979 assert id == to_string(other_user.id)
1982 test "getting following, hide_follows", %{conn: conn} do
1983 user = insert(:user, %{info: %{hide_follows: true}})
1984 other_user = insert(:user)
1985 {:ok, user} = User.follow(user, other_user)
1989 |> get("/api/v1/accounts/#{user.id}/following")
1991 assert [] == json_response(conn, 200)
1994 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1995 user = insert(:user, %{info: %{hide_follows: true}})
1996 other_user = insert(:user)
1997 {:ok, user} = User.follow(user, other_user)
2001 |> assign(:user, user)
2002 |> get("/api/v1/accounts/#{user.id}/following")
2004 refute [] == json_response(conn, 200)
2007 test "getting following, pagination", %{conn: conn} do
2008 user = insert(:user)
2009 following1 = insert(:user)
2010 following2 = insert(:user)
2011 following3 = insert(:user)
2012 {:ok, _} = User.follow(user, following1)
2013 {:ok, _} = User.follow(user, following2)
2014 {:ok, _} = User.follow(user, following3)
2018 |> assign(:user, user)
2022 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2024 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2025 assert id3 == following3.id
2026 assert id2 == following2.id
2030 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2032 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2033 assert id2 == following2.id
2034 assert id1 == following1.id
2038 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2040 assert [%{"id" => id2}] = json_response(res_conn, 200)
2041 assert id2 == following2.id
2043 assert [link_header] = get_resp_header(res_conn, "link")
2044 assert link_header =~ ~r/min_id=#{following2.id}/
2045 assert link_header =~ ~r/max_id=#{following2.id}/
2048 test "following / unfollowing a user", %{conn: conn} do
2049 user = insert(:user)
2050 other_user = insert(:user)
2054 |> assign(:user, user)
2055 |> post("/api/v1/accounts/#{other_user.id}/follow")
2057 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2059 user = User.get_cached_by_id(user.id)
2063 |> assign(:user, user)
2064 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2066 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2068 user = User.get_cached_by_id(user.id)
2072 |> assign(:user, user)
2073 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2075 assert %{"id" => id} = json_response(conn, 200)
2076 assert id == to_string(other_user.id)
2079 test "following without reblogs" do
2080 follower = insert(:user)
2081 followed = insert(:user)
2082 other_user = insert(:user)
2086 |> assign(:user, follower)
2087 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2089 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2091 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2092 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2096 |> assign(:user, User.get_cached_by_id(follower.id))
2097 |> get("/api/v1/timelines/home")
2099 assert [] == json_response(conn, 200)
2103 |> assign(:user, follower)
2104 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2106 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2110 |> assign(:user, User.get_cached_by_id(follower.id))
2111 |> get("/api/v1/timelines/home")
2113 expected_activity_id = reblog.id
2114 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2117 test "following / unfollowing errors" do
2118 user = insert(:user)
2122 |> assign(:user, user)
2125 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2126 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2129 user = User.get_cached_by_id(user.id)
2130 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2131 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2133 # self follow via uri
2134 user = User.get_cached_by_id(user.id)
2135 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2136 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2138 # follow non existing user
2139 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2140 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2142 # follow non existing user via uri
2143 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2144 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2146 # unfollow non existing user
2147 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2148 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2151 describe "mute/unmute" do
2152 test "with notifications", %{conn: conn} do
2153 user = insert(:user)
2154 other_user = insert(:user)
2158 |> assign(:user, user)
2159 |> post("/api/v1/accounts/#{other_user.id}/mute")
2161 response = json_response(conn, 200)
2163 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2164 user = User.get_cached_by_id(user.id)
2168 |> assign(:user, user)
2169 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2171 response = json_response(conn, 200)
2172 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2175 test "without notifications", %{conn: conn} do
2176 user = insert(:user)
2177 other_user = insert(:user)
2181 |> assign(:user, user)
2182 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2184 response = json_response(conn, 200)
2186 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2187 user = User.get_cached_by_id(user.id)
2191 |> assign(:user, user)
2192 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2194 response = json_response(conn, 200)
2195 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2199 describe "subscribing / unsubscribing" do
2200 test "subscribing / unsubscribing to a user", %{conn: conn} do
2201 user = insert(:user)
2202 subscription_target = insert(:user)
2206 |> assign(:user, user)
2207 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2209 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2213 |> assign(:user, user)
2214 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2216 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2220 describe "subscribing" do
2221 test "returns 404 when subscription_target not found", %{conn: conn} do
2222 user = insert(:user)
2226 |> assign(:user, user)
2227 |> post("/api/v1/pleroma/accounts/target_id/subscribe")
2229 assert %{"error" => "Record not found"} = json_response(conn, 404)
2233 describe "unsubscribing" 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/unsubscribe")
2242 assert %{"error" => "Record not found"} = json_response(conn, 404)
2246 test "getting a list of mutes", %{conn: conn} do
2247 user = insert(:user)
2248 other_user = insert(:user)
2250 {:ok, user} = User.mute(user, other_user)
2254 |> assign(:user, user)
2255 |> get("/api/v1/mutes")
2257 other_user_id = to_string(other_user.id)
2258 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2261 test "blocking / unblocking a user", %{conn: conn} do
2262 user = insert(:user)
2263 other_user = insert(:user)
2267 |> assign(:user, user)
2268 |> post("/api/v1/accounts/#{other_user.id}/block")
2270 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2272 user = User.get_cached_by_id(user.id)
2276 |> assign(:user, user)
2277 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2279 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2282 test "getting a list of blocks", %{conn: conn} do
2283 user = insert(:user)
2284 other_user = insert(:user)
2286 {:ok, user} = User.block(user, other_user)
2290 |> assign(:user, user)
2291 |> get("/api/v1/blocks")
2293 other_user_id = to_string(other_user.id)
2294 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2297 test "blocking / unblocking a domain", %{conn: conn} do
2298 user = insert(:user)
2299 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2303 |> assign(:user, user)
2304 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2306 assert %{} = json_response(conn, 200)
2307 user = User.get_cached_by_ap_id(user.ap_id)
2308 assert User.blocks?(user, other_user)
2312 |> assign(:user, user)
2313 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2315 assert %{} = json_response(conn, 200)
2316 user = User.get_cached_by_ap_id(user.ap_id)
2317 refute User.blocks?(user, other_user)
2320 test "getting a list of domain blocks", %{conn: conn} do
2321 user = insert(:user)
2323 {:ok, user} = User.block_domain(user, "bad.site")
2324 {:ok, user} = User.block_domain(user, "even.worse.site")
2328 |> assign(:user, user)
2329 |> get("/api/v1/domain_blocks")
2331 domain_blocks = json_response(conn, 200)
2333 assert "bad.site" in domain_blocks
2334 assert "even.worse.site" in domain_blocks
2337 test "unimplemented follow_requests, blocks, domain blocks" do
2338 user = insert(:user)
2340 ["blocks", "domain_blocks", "follow_requests"]
2341 |> Enum.each(fn endpoint ->
2344 |> assign(:user, user)
2345 |> get("/api/v1/#{endpoint}")
2347 assert [] = json_response(conn, 200)
2351 test "returns the favorites of a user", %{conn: conn} do
2352 user = insert(:user)
2353 other_user = insert(:user)
2355 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2356 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2358 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2362 |> assign(:user, user)
2363 |> get("/api/v1/favourites")
2365 assert [status] = json_response(first_conn, 200)
2366 assert status["id"] == to_string(activity.id)
2368 assert [{"link", _link_header}] =
2369 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2371 # Honours query params
2372 {:ok, second_activity} =
2373 CommonAPI.post(other_user, %{
2375 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2378 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2380 last_like = status["id"]
2384 |> assign(:user, user)
2385 |> get("/api/v1/favourites?since_id=#{last_like}")
2387 assert [second_status] = json_response(second_conn, 200)
2388 assert second_status["id"] == to_string(second_activity.id)
2392 |> assign(:user, user)
2393 |> get("/api/v1/favourites?limit=0")
2395 assert [] = json_response(third_conn, 200)
2398 describe "getting favorites timeline of specified user" do
2400 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2401 [current_user: current_user, user: user]
2404 test "returns list of statuses favorited by specified user", %{
2406 current_user: current_user,
2409 [activity | _] = insert_pair(:note_activity)
2410 CommonAPI.favorite(activity.id, user)
2414 |> assign(:user, current_user)
2415 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2416 |> json_response(:ok)
2420 assert length(response) == 1
2421 assert like["id"] == activity.id
2424 test "returns favorites for specified user_id when user is not logged in", %{
2428 activity = insert(:note_activity)
2429 CommonAPI.favorite(activity.id, user)
2433 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2434 |> json_response(:ok)
2436 assert length(response) == 1
2439 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2441 current_user: current_user,
2445 CommonAPI.post(current_user, %{
2446 "status" => "Hi @#{user.nickname}!",
2447 "visibility" => "direct"
2450 CommonAPI.favorite(direct.id, user)
2454 |> assign(:user, current_user)
2455 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2456 |> json_response(:ok)
2458 assert length(response) == 1
2460 anonymous_response =
2462 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2463 |> json_response(:ok)
2465 assert Enum.empty?(anonymous_response)
2468 test "does not return others' favorited DM when user is not one of recipients", %{
2470 current_user: current_user,
2473 user_two = insert(:user)
2476 CommonAPI.post(user_two, %{
2477 "status" => "Hi @#{user.nickname}!",
2478 "visibility" => "direct"
2481 CommonAPI.favorite(direct.id, user)
2485 |> assign(:user, current_user)
2486 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2487 |> json_response(:ok)
2489 assert Enum.empty?(response)
2492 test "paginates favorites using since_id and max_id", %{
2494 current_user: current_user,
2497 activities = insert_list(10, :note_activity)
2499 Enum.each(activities, fn activity ->
2500 CommonAPI.favorite(activity.id, user)
2503 third_activity = Enum.at(activities, 2)
2504 seventh_activity = Enum.at(activities, 6)
2508 |> assign(:user, current_user)
2509 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2510 since_id: third_activity.id,
2511 max_id: seventh_activity.id
2513 |> json_response(:ok)
2515 assert length(response) == 3
2516 refute third_activity in response
2517 refute seventh_activity in response
2520 test "limits favorites using limit parameter", %{
2522 current_user: current_user,
2526 |> insert_list(:note_activity)
2527 |> Enum.each(fn activity ->
2528 CommonAPI.favorite(activity.id, user)
2533 |> assign(:user, current_user)
2534 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2535 |> json_response(:ok)
2537 assert length(response) == 3
2540 test "returns empty response when user does not have any favorited statuses", %{
2542 current_user: current_user,
2547 |> assign(:user, current_user)
2548 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2549 |> json_response(:ok)
2551 assert Enum.empty?(response)
2554 test "returns 404 error when specified user is not exist", %{conn: conn} do
2555 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2557 assert json_response(conn, 404) == %{"error" => "Record not found"}
2560 test "returns 403 error when user has hidden own favorites", %{
2562 current_user: current_user
2564 user = insert(:user, %{info: %{hide_favorites: true}})
2565 activity = insert(:note_activity)
2566 CommonAPI.favorite(activity.id, user)
2570 |> assign(:user, current_user)
2571 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2573 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2576 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2577 user = insert(:user)
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 user.info.hide_favorites
2587 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2591 test "get instance information", %{conn: conn} do
2592 conn = get(conn, "/api/v1/instance")
2593 assert result = json_response(conn, 200)
2595 email = Config.get([:instance, :email])
2596 # Note: not checking for "max_toot_chars" since it's optional
2602 "email" => from_config_email,
2604 "streaming_api" => _
2609 "registrations" => _,
2613 assert email == from_config_email
2616 test "get instance stats", %{conn: conn} do
2617 user = insert(:user, %{local: true})
2619 user2 = insert(:user, %{local: true})
2620 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2622 insert(:user, %{local: false, nickname: "u@peer1.com"})
2623 insert(:user, %{local: false, nickname: "u@peer2.com"})
2625 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2627 # Stats should count users with missing or nil `info.deactivated` value
2628 user = User.get_cached_by_id(user.id)
2629 info_change = Changeset.change(user.info, %{deactivated: nil})
2633 |> Changeset.change()
2634 |> Changeset.put_embed(:info, info_change)
2635 |> User.update_and_set_cache()
2637 Pleroma.Stats.force_update()
2639 conn = get(conn, "/api/v1/instance")
2641 assert result = json_response(conn, 200)
2643 stats = result["stats"]
2646 assert stats["user_count"] == 1
2647 assert stats["status_count"] == 1
2648 assert stats["domain_count"] == 2
2651 test "get peers", %{conn: conn} do
2652 insert(:user, %{local: false, nickname: "u@peer1.com"})
2653 insert(:user, %{local: false, nickname: "u@peer2.com"})
2655 Pleroma.Stats.force_update()
2657 conn = get(conn, "/api/v1/instance/peers")
2659 assert result = json_response(conn, 200)
2661 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2664 test "put settings", %{conn: conn} do
2665 user = insert(:user)
2669 |> assign(:user, user)
2670 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2672 assert _result = json_response(conn, 200)
2674 user = User.get_cached_by_ap_id(user.ap_id)
2675 assert user.info.settings == %{"programming" => "socks"}
2678 describe "pinned statuses" do
2680 user = insert(:user)
2681 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2683 [user: user, activity: activity]
2686 clear_config([:instance, :max_pinned_statuses]) do
2687 Config.put([:instance, :max_pinned_statuses], 1)
2690 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2691 {:ok, _} = CommonAPI.pin(activity.id, user)
2695 |> assign(:user, user)
2696 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2697 |> json_response(200)
2699 id_str = to_string(activity.id)
2701 assert [%{"id" => ^id_str, "pinned" => true}] = result
2704 test "pin status", %{conn: conn, user: user, activity: activity} do
2705 id_str = to_string(activity.id)
2707 assert %{"id" => ^id_str, "pinned" => true} =
2709 |> assign(:user, user)
2710 |> post("/api/v1/statuses/#{activity.id}/pin")
2711 |> json_response(200)
2713 assert [%{"id" => ^id_str, "pinned" => true}] =
2715 |> assign(:user, user)
2716 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2717 |> json_response(200)
2720 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2721 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2725 |> assign(:user, user)
2726 |> post("/api/v1/statuses/#{dm.id}/pin")
2728 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2731 test "unpin status", %{conn: conn, user: user, activity: activity} do
2732 {:ok, _} = CommonAPI.pin(activity.id, user)
2734 id_str = to_string(activity.id)
2735 user = refresh_record(user)
2737 assert %{"id" => ^id_str, "pinned" => false} =
2739 |> assign(:user, user)
2740 |> post("/api/v1/statuses/#{activity.id}/unpin")
2741 |> json_response(200)
2745 |> assign(:user, user)
2746 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2747 |> json_response(200)
2750 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2753 |> assign(:user, user)
2754 |> post("/api/v1/statuses/1/unpin")
2756 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2759 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2760 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2762 id_str_one = to_string(activity_one.id)
2764 assert %{"id" => ^id_str_one, "pinned" => true} =
2766 |> assign(:user, user)
2767 |> post("/api/v1/statuses/#{id_str_one}/pin")
2768 |> json_response(200)
2770 user = refresh_record(user)
2772 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2774 |> assign(:user, user)
2775 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2776 |> json_response(400)
2782 Config.put([:rich_media, :enabled], true)
2784 user = insert(:user)
2788 test "returns rich-media card", %{conn: conn, user: user} do
2789 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2792 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2793 "provider_name" => "example.com",
2794 "provider_url" => "https://example.com",
2795 "title" => "The Rock",
2797 "url" => "https://example.com/ogp",
2799 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2802 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2803 "title" => "The Rock",
2804 "type" => "video.movie",
2805 "url" => "https://example.com/ogp",
2807 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2814 |> get("/api/v1/statuses/#{activity.id}/card")
2815 |> json_response(200)
2817 assert response == card_data
2819 # works with private posts
2821 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2825 |> assign(:user, user)
2826 |> get("/api/v1/statuses/#{activity.id}/card")
2827 |> json_response(200)
2829 assert response_two == card_data
2832 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2834 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2838 |> get("/api/v1/statuses/#{activity.id}/card")
2839 |> json_response(:ok)
2841 assert response == %{
2843 "title" => "Pleroma",
2844 "description" => "",
2846 "provider_name" => "example.com",
2847 "provider_url" => "https://example.com",
2848 "url" => "https://example.com/ogp-missing-data",
2851 "title" => "Pleroma",
2852 "type" => "website",
2853 "url" => "https://example.com/ogp-missing-data"
2859 test "returns empty object when id invalid", %{conn: conn} do
2862 |> get("/api/v1/statuses/9eoozpwTul5mjSEDRI/card")
2863 |> json_response(200)
2865 assert response == %{}
2870 user = insert(:user)
2871 for_user = insert(:user)
2874 CommonAPI.post(user, %{
2875 "status" => "heweoo?"
2879 CommonAPI.post(user, %{
2880 "status" => "heweoo!"
2885 |> assign(:user, for_user)
2886 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2888 assert json_response(response1, 200)["bookmarked"] == true
2892 |> assign(:user, for_user)
2893 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2895 assert json_response(response2, 200)["bookmarked"] == true
2899 |> assign(:user, for_user)
2900 |> get("/api/v1/bookmarks")
2902 assert [json_response(response2, 200), json_response(response1, 200)] ==
2903 json_response(bookmarks, 200)
2907 |> assign(:user, for_user)
2908 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2910 assert json_response(response1, 200)["bookmarked"] == false
2914 |> assign(:user, for_user)
2915 |> get("/api/v1/bookmarks")
2917 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2920 describe "conversation muting" do
2922 post_user = insert(:user)
2923 user = insert(:user)
2925 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2927 [user: user, activity: activity]
2930 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2931 id_str = to_string(activity.id)
2933 assert %{"id" => ^id_str, "muted" => true} =
2935 |> assign(:user, user)
2936 |> post("/api/v1/statuses/#{activity.id}/mute")
2937 |> json_response(200)
2940 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2941 {:ok, _} = CommonAPI.add_mute(user, activity)
2945 |> assign(:user, user)
2946 |> post("/api/v1/statuses/#{activity.id}/mute")
2948 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2951 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2952 {:ok, _} = CommonAPI.add_mute(user, activity)
2954 id_str = to_string(activity.id)
2955 user = refresh_record(user)
2957 assert %{"id" => ^id_str, "muted" => false} =
2959 |> assign(:user, user)
2960 |> post("/api/v1/statuses/#{activity.id}/unmute")
2961 |> json_response(200)
2965 describe "reports" do
2967 reporter = insert(:user)
2968 target_user = insert(:user)
2970 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2972 [reporter: reporter, target_user: target_user, activity: activity]
2975 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2976 assert %{"action_taken" => false, "id" => _} =
2978 |> assign(:user, reporter)
2979 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2980 |> json_response(200)
2983 test "submit a report with statuses and comment", %{
2986 target_user: target_user,
2989 assert %{"action_taken" => false, "id" => _} =
2991 |> assign(:user, reporter)
2992 |> post("/api/v1/reports", %{
2993 "account_id" => target_user.id,
2994 "status_ids" => [activity.id],
2995 "comment" => "bad status!",
2996 "forward" => "false"
2998 |> json_response(200)
3001 test "account_id is required", %{
3006 assert %{"error" => "Valid `account_id` required"} =
3008 |> assign(:user, reporter)
3009 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3010 |> json_response(400)
3013 test "comment must be up to the size specified in the config", %{
3016 target_user: target_user
3018 max_size = Config.get([:instance, :max_report_comment_size], 1000)
3019 comment = String.pad_trailing("a", max_size + 1, "a")
3021 error = %{"error" => "Comment must be up to #{max_size} characters"}
3025 |> assign(:user, reporter)
3026 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3027 |> json_response(400)
3030 test "returns error when account is not exist", %{
3037 |> assign(:user, reporter)
3038 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3040 assert json_response(conn, 400) == %{"error" => "Account not found"}
3044 describe "link headers" do
3045 test "preserves parameters in link headers", %{conn: conn} do
3046 user = insert(:user)
3047 other_user = insert(:user)
3050 CommonAPI.post(other_user, %{
3051 "status" => "hi @#{user.nickname}",
3052 "visibility" => "public"
3056 CommonAPI.post(other_user, %{
3057 "status" => "hi @#{user.nickname}",
3058 "visibility" => "public"
3061 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3062 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3066 |> assign(:user, user)
3067 |> get("/api/v1/notifications", %{media_only: true})
3069 assert [link_header] = get_resp_header(conn, "link")
3070 assert link_header =~ ~r/media_only=true/
3071 assert link_header =~ ~r/min_id=#{notification2.id}/
3072 assert link_header =~ ~r/max_id=#{notification1.id}/
3076 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3077 # Need to set an old-style integer ID to reproduce the problem
3078 # (these are no longer assigned to new accounts but were preserved
3079 # for existing accounts during the migration to flakeIDs)
3080 user_one = insert(:user, %{id: 1212})
3081 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3085 |> get("/api/v1/accounts/#{user_one.id}")
3089 |> get("/api/v1/accounts/#{user_two.nickname}")
3093 |> get("/api/v1/accounts/#{user_two.id}")
3095 acc_one = json_response(resp_one, 200)
3096 acc_two = json_response(resp_two, 200)
3097 acc_three = json_response(resp_three, 200)
3098 refute acc_one == acc_two
3099 assert acc_two == acc_three
3102 describe "custom emoji" do
3103 test "with tags", %{conn: conn} do
3106 |> get("/api/v1/custom_emojis")
3107 |> json_response(200)
3109 assert Map.has_key?(emoji, "shortcode")
3110 assert Map.has_key?(emoji, "static_url")
3111 assert Map.has_key?(emoji, "tags")
3112 assert is_list(emoji["tags"])
3113 assert Map.has_key?(emoji, "category")
3114 assert Map.has_key?(emoji, "url")
3115 assert Map.has_key?(emoji, "visible_in_picker")
3119 describe "index/2 redirections" do
3120 setup %{conn: conn} do
3124 signing_salt: "cooldude"
3129 |> Plug.Session.call(Plug.Session.init(session_opts))
3132 test_path = "/web/statuses/test"
3133 %{conn: conn, path: test_path}
3136 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3137 conn = get(conn, path)
3139 assert conn.status == 302
3140 assert redirected_to(conn) == "/web/login"
3143 test "redirects not logged-in users to the login page on private instances", %{
3147 Config.put([:instance, :public], false)
3149 conn = get(conn, path)
3151 assert conn.status == 302
3152 assert redirected_to(conn) == "/web/login"
3155 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3156 token = insert(:oauth_token)
3160 |> assign(:user, token.user)
3161 |> put_session(:oauth_token, token.token)
3164 assert conn.status == 200
3167 test "saves referer path to session", %{conn: conn, path: path} do
3168 conn = get(conn, path)
3169 return_to = Plug.Conn.get_session(conn, :return_to)
3171 assert return_to == path
3174 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3175 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3176 auth = insert(:oauth_authorization, app: app)
3180 |> put_session(:return_to, path)
3181 |> get("/web/login", %{code: auth.token})
3183 assert conn.status == 302
3184 assert redirected_to(conn) == path
3188 describe "GET /web/login" do
3189 test "redirects to /oauth/authorize", %{conn: conn} do
3190 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3191 conn = get(conn, "/web/login", %{})
3193 assert conn.status == 302
3195 assert redirected_to(conn) ==
3196 "/oauth/authorize?response_type=code&client_id=#{app.client_id}&redirect_uri=.&scope=read+write+follow+push"
3199 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3200 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3201 auth = insert(:oauth_authorization, app: app)
3203 conn = get(conn, "/web/login", %{code: auth.token})
3205 assert conn.status == 302
3206 assert redirected_to(conn) == "/web/getting-started"
3209 test "redirects to the getting-started page when user assigned", %{conn: conn} do
3210 user = insert(:user)
3214 |> assign(:user, user)
3215 |> get("/web/login", %{})
3217 assert conn.status == 302
3218 assert redirected_to(conn) == "/web/getting-started"
3222 describe "scheduled activities" do
3223 test "creates a scheduled activity", %{conn: conn} do
3224 user = insert(:user)
3225 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3229 |> assign(:user, user)
3230 |> post("/api/v1/statuses", %{
3231 "status" => "scheduled",
3232 "scheduled_at" => scheduled_at
3235 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3236 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3237 assert [] == Repo.all(Activity)
3240 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3241 user = insert(:user)
3242 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3244 file = %Plug.Upload{
3245 content_type: "image/jpg",
3246 path: Path.absname("test/fixtures/image.jpg"),
3247 filename: "an_image.jpg"
3250 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3254 |> assign(:user, user)
3255 |> post("/api/v1/statuses", %{
3256 "media_ids" => [to_string(upload.id)],
3257 "status" => "scheduled",
3258 "scheduled_at" => scheduled_at
3261 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3262 assert %{"type" => "image"} = media_attachment
3265 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3267 user = insert(:user)
3270 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3274 |> assign(:user, user)
3275 |> post("/api/v1/statuses", %{
3276 "status" => "not scheduled",
3277 "scheduled_at" => scheduled_at
3280 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3281 assert [] == Repo.all(ScheduledActivity)
3284 test "returns error when daily user limit is exceeded", %{conn: conn} do
3285 user = insert(:user)
3288 NaiveDateTime.utc_now()
3289 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3290 |> NaiveDateTime.to_iso8601()
3292 attrs = %{params: %{}, scheduled_at: today}
3293 {:ok, _} = ScheduledActivity.create(user, attrs)
3294 {:ok, _} = ScheduledActivity.create(user, attrs)
3298 |> assign(:user, user)
3299 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3301 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3304 test "returns error when total user limit is exceeded", %{conn: conn} do
3305 user = insert(:user)
3308 NaiveDateTime.utc_now()
3309 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3310 |> NaiveDateTime.to_iso8601()
3313 NaiveDateTime.utc_now()
3314 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3315 |> NaiveDateTime.to_iso8601()
3317 attrs = %{params: %{}, scheduled_at: today}
3318 {:ok, _} = ScheduledActivity.create(user, attrs)
3319 {:ok, _} = ScheduledActivity.create(user, attrs)
3320 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3324 |> assign(:user, user)
3325 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3327 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3330 test "shows scheduled activities", %{conn: conn} do
3331 user = insert(:user)
3332 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3333 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3334 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3335 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3339 |> assign(:user, user)
3344 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3346 result = json_response(conn_res, 200)
3347 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3352 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3354 result = json_response(conn_res, 200)
3355 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3360 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3362 result = json_response(conn_res, 200)
3363 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3366 test "shows a scheduled activity", %{conn: conn} do
3367 user = insert(:user)
3368 scheduled_activity = insert(:scheduled_activity, user: user)
3372 |> assign(:user, user)
3373 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3375 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3376 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3380 |> assign(:user, user)
3381 |> get("/api/v1/scheduled_statuses/404")
3383 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3386 test "updates a scheduled activity", %{conn: conn} do
3387 user = insert(:user)
3388 scheduled_activity = insert(:scheduled_activity, user: user)
3391 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3395 |> assign(:user, user)
3396 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3397 scheduled_at: new_scheduled_at
3400 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3401 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3405 |> assign(:user, user)
3406 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3408 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3411 test "deletes a scheduled activity", %{conn: conn} do
3412 user = insert(:user)
3413 scheduled_activity = insert(:scheduled_activity, user: user)
3417 |> assign(:user, user)
3418 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3420 assert %{} = json_response(res_conn, 200)
3421 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3425 |> assign(:user, user)
3426 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3428 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3432 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3433 user1 = insert(:user)
3434 user2 = insert(:user)
3435 user3 = insert(:user)
3437 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3439 # Reply to status from another user
3442 |> assign(:user, user2)
3443 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3445 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3447 activity = Activity.get_by_id_with_object(id)
3449 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3450 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3452 # Reblog from the third user
3455 |> assign(:user, user3)
3456 |> post("/api/v1/statuses/#{activity.id}/reblog")
3458 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3459 json_response(conn2, 200)
3461 assert to_string(activity.id) == id
3463 # Getting third user status
3466 |> assign(:user, user3)
3467 |> get("api/v1/timelines/home")
3469 [reblogged_activity] = json_response(conn3, 200)
3471 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3473 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3474 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3477 describe "create account by app" do
3481 email: "lain@example.org",
3482 password: "PlzDontHackLain",
3486 [valid_params: valid_params]
3489 test "Account registration via Application", %{conn: conn} do
3492 |> post("/api/v1/apps", %{
3493 client_name: "client_name",
3494 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3495 scopes: "read, write, follow"
3499 "client_id" => client_id,
3500 "client_secret" => client_secret,
3502 "name" => "client_name",
3503 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3506 } = json_response(conn, 200)
3510 |> post("/oauth/token", %{
3511 grant_type: "client_credentials",
3512 client_id: client_id,
3513 client_secret: client_secret
3516 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3517 json_response(conn, 200)
3520 token_from_db = Repo.get_by(Token, token: token)
3521 assert token_from_db
3523 assert scope == "read write follow"
3527 |> put_req_header("authorization", "Bearer " <> token)
3528 |> post("/api/v1/accounts", %{
3530 email: "lain@example.org",
3531 password: "PlzDontHackLain",
3537 "access_token" => token,
3538 "created_at" => _created_at,
3540 "token_type" => "Bearer"
3541 } = json_response(conn, 200)
3543 token_from_db = Repo.get_by(Token, token: token)
3544 assert token_from_db
3545 token_from_db = Repo.preload(token_from_db, :user)
3546 assert token_from_db.user
3548 assert token_from_db.user.info.confirmation_pending
3551 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
3552 _user = insert(:user, email: "lain@example.org")
3553 app_token = insert(:oauth_token, user: nil)
3557 |> put_req_header("authorization", "Bearer " <> app_token.token)
3559 res = post(conn, "/api/v1/accounts", valid_params)
3560 assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
3563 test "rate limit", %{conn: conn} do
3564 app_token = insert(:oauth_token, user: nil)
3567 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3568 |> Map.put(:remote_ip, {15, 15, 15, 15})
3573 |> post("/api/v1/accounts", %{
3574 username: "#{i}lain",
3575 email: "#{i}lain@example.org",
3576 password: "PlzDontHackLain",
3581 "access_token" => token,
3582 "created_at" => _created_at,
3584 "token_type" => "Bearer"
3585 } = json_response(conn, 200)
3587 token_from_db = Repo.get_by(Token, token: token)
3588 assert token_from_db
3589 token_from_db = Repo.preload(token_from_db, :user)
3590 assert token_from_db.user
3592 assert token_from_db.user.info.confirmation_pending
3597 |> post("/api/v1/accounts", %{
3599 email: "6lain@example.org",
3600 password: "PlzDontHackLain",
3604 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3607 test "returns bad_request if missing required params", %{
3609 valid_params: valid_params
3611 app_token = insert(:oauth_token, user: nil)
3615 |> put_req_header("authorization", "Bearer " <> app_token.token)
3617 res = post(conn, "/api/v1/accounts", valid_params)
3618 assert json_response(res, 200)
3620 Enum.each(valid_params, fn {attr, _} ->
3625 {:rand.uniform(15), :rand.uniform(15), :rand.uniform(15), :rand.uniform(15)}
3627 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
3629 assert json_response(res, 400) == %{"error" => "Missing parameters"}
3633 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
3636 |> put_req_header("authorization", "Bearer " <> "invalid-token")
3638 res = post(conn, "/api/v1/accounts", valid_params)
3639 assert json_response(res, 403) == %{"error" => "Invalid credentials"}
3643 describe "GET /api/v1/polls/:id" do
3644 test "returns poll entity for object id", %{conn: conn} do
3645 user = insert(:user)
3648 CommonAPI.post(user, %{
3649 "status" => "Pleroma does",
3650 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3653 object = Object.normalize(activity)
3657 |> assign(:user, user)
3658 |> get("/api/v1/polls/#{object.id}")
3660 response = json_response(conn, 200)
3661 id = to_string(object.id)
3662 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3665 test "does not expose polls for private statuses", %{conn: conn} do
3666 user = insert(:user)
3667 other_user = insert(:user)
3670 CommonAPI.post(user, %{
3671 "status" => "Pleroma does",
3672 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3673 "visibility" => "private"
3676 object = Object.normalize(activity)
3680 |> assign(:user, other_user)
3681 |> get("/api/v1/polls/#{object.id}")
3683 assert json_response(conn, 404)
3687 describe "POST /api/v1/polls/:id/votes" do
3688 test "votes are added to the poll", %{conn: conn} do
3689 user = insert(:user)
3690 other_user = insert(:user)
3693 CommonAPI.post(user, %{
3694 "status" => "A very delicious sandwich",
3696 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3702 object = Object.normalize(activity)
3706 |> assign(:user, other_user)
3707 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3709 assert json_response(conn, 200)
3710 object = Object.get_by_id(object.id)
3712 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3717 test "author can't vote", %{conn: conn} do
3718 user = insert(:user)
3721 CommonAPI.post(user, %{
3722 "status" => "Am I cute?",
3723 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3726 object = Object.normalize(activity)
3729 |> assign(:user, user)
3730 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3731 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3733 object = Object.get_by_id(object.id)
3735 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3738 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3739 user = insert(:user)
3740 other_user = insert(:user)
3743 CommonAPI.post(user, %{
3744 "status" => "The glass is",
3745 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3748 object = Object.normalize(activity)
3751 |> assign(:user, other_user)
3752 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3753 |> json_response(422) == %{"error" => "Too many choices"}
3755 object = Object.get_by_id(object.id)
3757 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3762 test "does not allow choice index to be greater than options count", %{conn: conn} do
3763 user = insert(:user)
3764 other_user = insert(:user)
3767 CommonAPI.post(user, %{
3768 "status" => "Am I cute?",
3769 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3772 object = Object.normalize(activity)
3776 |> assign(:user, other_user)
3777 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3779 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3782 test "returns 404 error when object is not exist", %{conn: conn} do
3783 user = insert(:user)
3787 |> assign(:user, user)
3788 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3790 assert json_response(conn, 404) == %{"error" => "Record not found"}
3793 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3794 user = insert(:user)
3795 other_user = insert(:user)
3798 CommonAPI.post(user, %{
3799 "status" => "Am I cute?",
3800 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3801 "visibility" => "private"
3804 object = Object.normalize(activity)
3808 |> assign(:user, other_user)
3809 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3811 assert json_response(conn, 404) == %{"error" => "Record not found"}
3815 describe "GET /api/v1/statuses/:id/favourited_by" do
3817 user = insert(:user)
3818 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3822 |> assign(:user, user)
3824 [conn: conn, activity: activity]
3827 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3828 other_user = insert(:user)
3829 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3833 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3834 |> json_response(:ok)
3836 [%{"id" => id}] = response
3838 assert id == other_user.id
3841 test "returns empty array when status has not been favorited yet", %{
3847 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3848 |> json_response(:ok)
3850 assert Enum.empty?(response)
3853 test "does not return users who have favorited the status but are blocked", %{
3854 conn: %{assigns: %{user: user}} = conn,
3857 other_user = insert(:user)
3858 {:ok, user} = User.block(user, other_user)
3860 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3864 |> assign(:user, user)
3865 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3866 |> json_response(:ok)
3868 assert Enum.empty?(response)
3871 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3872 other_user = insert(:user)
3873 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3877 |> assign(:user, nil)
3878 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3879 |> json_response(:ok)
3881 [%{"id" => id}] = response
3882 assert id == other_user.id
3886 describe "GET /api/v1/statuses/:id/reblogged_by" do
3888 user = insert(:user)
3889 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3893 |> assign(:user, user)
3895 [conn: conn, activity: activity]
3898 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3899 other_user = insert(:user)
3900 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3904 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3905 |> json_response(:ok)
3907 [%{"id" => id}] = response
3909 assert id == other_user.id
3912 test "returns empty array when status has not been reblogged yet", %{
3918 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3919 |> json_response(:ok)
3921 assert Enum.empty?(response)
3924 test "does not return users who have reblogged the status but are blocked", %{
3925 conn: %{assigns: %{user: user}} = conn,
3928 other_user = insert(:user)
3929 {:ok, user} = User.block(user, other_user)
3931 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3935 |> assign(:user, user)
3936 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3937 |> json_response(:ok)
3939 assert Enum.empty?(response)
3942 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3943 other_user = insert(:user)
3944 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3948 |> assign(:user, nil)
3949 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3950 |> json_response(:ok)
3952 [%{"id" => id}] = response
3953 assert id == other_user.id
3957 describe "POST /auth/password, with valid parameters" do
3958 setup %{conn: conn} do
3959 user = insert(:user)
3960 conn = post(conn, "/auth/password?email=#{user.email}")
3961 %{conn: conn, user: user}
3964 test "it returns 204", %{conn: conn} do
3965 assert json_response(conn, :no_content)
3968 test "it creates a PasswordResetToken record for user", %{user: user} do
3969 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3973 test "it sends an email to user", %{user: user} do
3974 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3976 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3977 notify_email = Config.get([:instance, :notify_email])
3978 instance_name = Config.get([:instance, :name])
3981 from: {instance_name, notify_email},
3982 to: {user.name, user.email},
3983 html_body: email.html_body
3988 describe "POST /auth/password, with invalid parameters" do
3990 user = insert(:user)
3994 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3995 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3996 assert conn.status == 404
3997 assert conn.resp_body == ""
4000 test "it returns 400 when user is not local", %{conn: conn, user: user} do
4001 {:ok, user} = Repo.update(Changeset.change(user, local: false))
4002 conn = post(conn, "/auth/password?email=#{user.email}")
4003 assert conn.status == 400
4004 assert conn.resp_body == ""
4008 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
4010 user = insert(:user)
4011 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
4015 |> Changeset.change()
4016 |> Changeset.put_embed(:info, info_change)
4019 assert user.info.confirmation_pending
4024 clear_config([:instance, :account_activation_required]) do
4025 Config.put([:instance, :account_activation_required], true)
4028 test "resend account confirmation email", %{conn: conn, user: user} do
4030 |> assign(:user, user)
4031 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
4032 |> json_response(:no_content)
4034 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
4035 notify_email = Config.get([:instance, :notify_email])
4036 instance_name = Config.get([:instance, :name])
4039 from: {instance_name, notify_email},
4040 to: {user.name, user.email},
4041 html_body: email.html_body
4046 describe "GET /api/v1/suggestions" do
4048 user = insert(:user)
4049 other_user = insert(:user)
4050 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
4051 url500 = "http://test500?#{host}&#{user.nickname}"
4052 url200 = "http://test200?#{host}&#{user.nickname}"
4055 %{method: :get, url: ^url500} ->
4056 %Tesla.Env{status: 500, body: "bad request"}
4058 %{method: :get, url: ^url200} ->
4062 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4064 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4068 [user: user, other_user: other_user]
4071 clear_config(:suggestions)
4073 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4074 Config.put([:suggestions, :enabled], false)
4078 |> assign(:user, user)
4079 |> get("/api/v1/suggestions")
4080 |> json_response(200)
4085 test "returns error", %{conn: conn, user: user} do
4086 Config.put([:suggestions, :enabled], true)
4087 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4091 |> assign(:user, user)
4092 |> get("/api/v1/suggestions")
4093 |> json_response(500)
4095 assert res == "Something went wrong"
4098 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4099 Config.put([:suggestions, :enabled], true)
4100 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4104 |> assign(:user, user)
4105 |> get("/api/v1/suggestions")
4106 |> json_response(200)
4111 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4112 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4116 "acct" => other_user.ap_id,
4117 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4118 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4119 "id" => other_user.id
4125 describe "PUT /api/v1/media/:id" do
4127 actor = insert(:user)
4129 file = %Plug.Upload{
4130 content_type: "image/jpg",
4131 path: Path.absname("test/fixtures/image.jpg"),
4132 filename: "an_image.jpg"
4135 {:ok, %Object{} = object} =
4138 actor: User.ap_id(actor),
4139 description: "test-m"
4142 [actor: actor, object: object]
4145 test "updates name of media", %{conn: conn, actor: actor, object: object} do
4148 |> assign(:user, actor)
4149 |> put("/api/v1/media/#{object.id}", %{"description" => "test-media"})
4150 |> json_response(:ok)
4152 assert media["description"] == "test-media"
4153 assert refresh_record(object).data["name"] == "test-media"
4156 test "returns error wheb request is bad", %{conn: conn, actor: actor, object: object} do
4159 |> assign(:user, actor)
4160 |> put("/api/v1/media/#{object.id}", %{})
4161 |> json_response(400)
4163 assert media == %{"error" => "bad_request"}
4167 describe "DELETE /auth/sign_out" do
4168 test "redirect to root page", %{conn: conn} do
4169 user = insert(:user)
4173 |> assign(:user, user)
4174 |> delete("/auth/sign_out")
4176 assert conn.status == 302
4177 assert redirected_to(conn) == "/"
4181 describe "GET /api/v1/accounts/:id/lists - account_lists" do
4182 test "returns lists to which the account belongs", %{conn: conn} do
4183 user = insert(:user)
4184 other_user = insert(:user)
4185 assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user)
4186 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
4190 |> assign(:user, user)
4191 |> get("/api/v1/accounts/#{other_user.id}/lists")
4192 |> json_response(200)
4194 assert res == [%{"id" => to_string(list.id), "title" => "Test List"}]
4198 describe "empty_array, stubs for mastodon api" do
4199 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
4200 user = insert(:user)
4204 |> assign(:user, user)
4205 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
4206 |> json_response(200)
4211 test "GET /api/v1/endorsements", %{conn: conn} do
4212 user = insert(:user)
4216 |> assign(:user, user)
4217 |> get("/api/v1/endorsements")
4218 |> json_response(200)
4223 test "GET /api/v1/trends", %{conn: conn} do
4224 user = insert(:user)
4228 |> assign(:user, user)
4229 |> get("/api/v1/trends")
4230 |> json_response(200)