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 == %{}
2868 test "returns empty object when id isn't FlakeID", %{conn: conn} do
2871 |> get("/api/v1/statuses/3ebbadd1-eb14-4e20-8118/card")
2872 |> json_response(200)
2874 assert response == %{}
2879 user = insert(:user)
2880 for_user = insert(:user)
2883 CommonAPI.post(user, %{
2884 "status" => "heweoo?"
2888 CommonAPI.post(user, %{
2889 "status" => "heweoo!"
2894 |> assign(:user, for_user)
2895 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2897 assert json_response(response1, 200)["bookmarked"] == true
2901 |> assign(:user, for_user)
2902 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2904 assert json_response(response2, 200)["bookmarked"] == true
2908 |> assign(:user, for_user)
2909 |> get("/api/v1/bookmarks")
2911 assert [json_response(response2, 200), json_response(response1, 200)] ==
2912 json_response(bookmarks, 200)
2916 |> assign(:user, for_user)
2917 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2919 assert json_response(response1, 200)["bookmarked"] == false
2923 |> assign(:user, for_user)
2924 |> get("/api/v1/bookmarks")
2926 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2929 describe "conversation muting" do
2931 post_user = insert(:user)
2932 user = insert(:user)
2934 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2936 [user: user, activity: activity]
2939 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2940 id_str = to_string(activity.id)
2942 assert %{"id" => ^id_str, "muted" => true} =
2944 |> assign(:user, user)
2945 |> post("/api/v1/statuses/#{activity.id}/mute")
2946 |> json_response(200)
2949 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2950 {:ok, _} = CommonAPI.add_mute(user, activity)
2954 |> assign(:user, user)
2955 |> post("/api/v1/statuses/#{activity.id}/mute")
2957 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2960 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2961 {:ok, _} = CommonAPI.add_mute(user, activity)
2963 id_str = to_string(activity.id)
2964 user = refresh_record(user)
2966 assert %{"id" => ^id_str, "muted" => false} =
2968 |> assign(:user, user)
2969 |> post("/api/v1/statuses/#{activity.id}/unmute")
2970 |> json_response(200)
2974 describe "reports" do
2976 reporter = insert(:user)
2977 target_user = insert(:user)
2979 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2981 [reporter: reporter, target_user: target_user, activity: activity]
2984 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2985 assert %{"action_taken" => false, "id" => _} =
2987 |> assign(:user, reporter)
2988 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2989 |> json_response(200)
2992 test "submit a report with statuses and comment", %{
2995 target_user: target_user,
2998 assert %{"action_taken" => false, "id" => _} =
3000 |> assign(:user, reporter)
3001 |> post("/api/v1/reports", %{
3002 "account_id" => target_user.id,
3003 "status_ids" => [activity.id],
3004 "comment" => "bad status!",
3005 "forward" => "false"
3007 |> json_response(200)
3010 test "account_id is required", %{
3015 assert %{"error" => "Valid `account_id` required"} =
3017 |> assign(:user, reporter)
3018 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3019 |> json_response(400)
3022 test "comment must be up to the size specified in the config", %{
3025 target_user: target_user
3027 max_size = Config.get([:instance, :max_report_comment_size], 1000)
3028 comment = String.pad_trailing("a", max_size + 1, "a")
3030 error = %{"error" => "Comment must be up to #{max_size} characters"}
3034 |> assign(:user, reporter)
3035 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3036 |> json_response(400)
3039 test "returns error when account is not exist", %{
3046 |> assign(:user, reporter)
3047 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3049 assert json_response(conn, 400) == %{"error" => "Account not found"}
3053 describe "link headers" do
3054 test "preserves parameters in link headers", %{conn: conn} do
3055 user = insert(:user)
3056 other_user = insert(:user)
3059 CommonAPI.post(other_user, %{
3060 "status" => "hi @#{user.nickname}",
3061 "visibility" => "public"
3065 CommonAPI.post(other_user, %{
3066 "status" => "hi @#{user.nickname}",
3067 "visibility" => "public"
3070 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3071 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3075 |> assign(:user, user)
3076 |> get("/api/v1/notifications", %{media_only: true})
3078 assert [link_header] = get_resp_header(conn, "link")
3079 assert link_header =~ ~r/media_only=true/
3080 assert link_header =~ ~r/min_id=#{notification2.id}/
3081 assert link_header =~ ~r/max_id=#{notification1.id}/
3085 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3086 # Need to set an old-style integer ID to reproduce the problem
3087 # (these are no longer assigned to new accounts but were preserved
3088 # for existing accounts during the migration to flakeIDs)
3089 user_one = insert(:user, %{id: 1212})
3090 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3094 |> get("/api/v1/accounts/#{user_one.id}")
3098 |> get("/api/v1/accounts/#{user_two.nickname}")
3102 |> get("/api/v1/accounts/#{user_two.id}")
3104 acc_one = json_response(resp_one, 200)
3105 acc_two = json_response(resp_two, 200)
3106 acc_three = json_response(resp_three, 200)
3107 refute acc_one == acc_two
3108 assert acc_two == acc_three
3111 describe "custom emoji" do
3112 test "with tags", %{conn: conn} do
3115 |> get("/api/v1/custom_emojis")
3116 |> json_response(200)
3118 assert Map.has_key?(emoji, "shortcode")
3119 assert Map.has_key?(emoji, "static_url")
3120 assert Map.has_key?(emoji, "tags")
3121 assert is_list(emoji["tags"])
3122 assert Map.has_key?(emoji, "category")
3123 assert Map.has_key?(emoji, "url")
3124 assert Map.has_key?(emoji, "visible_in_picker")
3128 describe "index/2 redirections" do
3129 setup %{conn: conn} do
3133 signing_salt: "cooldude"
3138 |> Plug.Session.call(Plug.Session.init(session_opts))
3141 test_path = "/web/statuses/test"
3142 %{conn: conn, path: test_path}
3145 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3146 conn = get(conn, path)
3148 assert conn.status == 302
3149 assert redirected_to(conn) == "/web/login"
3152 test "redirects not logged-in users to the login page on private instances", %{
3156 Config.put([:instance, :public], false)
3158 conn = get(conn, path)
3160 assert conn.status == 302
3161 assert redirected_to(conn) == "/web/login"
3164 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3165 token = insert(:oauth_token)
3169 |> assign(:user, token.user)
3170 |> put_session(:oauth_token, token.token)
3173 assert conn.status == 200
3176 test "saves referer path to session", %{conn: conn, path: path} do
3177 conn = get(conn, path)
3178 return_to = Plug.Conn.get_session(conn, :return_to)
3180 assert return_to == path
3183 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3184 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3185 auth = insert(:oauth_authorization, app: app)
3189 |> put_session(:return_to, path)
3190 |> get("/web/login", %{code: auth.token})
3192 assert conn.status == 302
3193 assert redirected_to(conn) == path
3197 describe "GET /web/login" do
3198 test "redirects to /oauth/authorize", %{conn: conn} do
3199 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3200 conn = get(conn, "/web/login", %{})
3202 assert conn.status == 302
3204 assert redirected_to(conn) ==
3205 "/oauth/authorize?response_type=code&client_id=#{app.client_id}&redirect_uri=.&scope=read+write+follow+push"
3208 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3209 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3210 auth = insert(:oauth_authorization, app: app)
3212 conn = get(conn, "/web/login", %{code: auth.token})
3214 assert conn.status == 302
3215 assert redirected_to(conn) == "/web/getting-started"
3218 test "redirects to the getting-started page when user assigned", %{conn: conn} do
3219 user = insert(:user)
3223 |> assign(:user, user)
3224 |> get("/web/login", %{})
3226 assert conn.status == 302
3227 assert redirected_to(conn) == "/web/getting-started"
3231 describe "scheduled activities" do
3232 test "creates a scheduled activity", %{conn: conn} do
3233 user = insert(:user)
3234 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3238 |> assign(:user, user)
3239 |> post("/api/v1/statuses", %{
3240 "status" => "scheduled",
3241 "scheduled_at" => scheduled_at
3244 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3245 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3246 assert [] == Repo.all(Activity)
3249 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3250 user = insert(:user)
3251 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3253 file = %Plug.Upload{
3254 content_type: "image/jpg",
3255 path: Path.absname("test/fixtures/image.jpg"),
3256 filename: "an_image.jpg"
3259 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3263 |> assign(:user, user)
3264 |> post("/api/v1/statuses", %{
3265 "media_ids" => [to_string(upload.id)],
3266 "status" => "scheduled",
3267 "scheduled_at" => scheduled_at
3270 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3271 assert %{"type" => "image"} = media_attachment
3274 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3276 user = insert(:user)
3279 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3283 |> assign(:user, user)
3284 |> post("/api/v1/statuses", %{
3285 "status" => "not scheduled",
3286 "scheduled_at" => scheduled_at
3289 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3290 assert [] == Repo.all(ScheduledActivity)
3293 test "returns error when daily user limit is exceeded", %{conn: conn} do
3294 user = insert(:user)
3297 NaiveDateTime.utc_now()
3298 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3299 |> NaiveDateTime.to_iso8601()
3301 attrs = %{params: %{}, scheduled_at: today}
3302 {:ok, _} = ScheduledActivity.create(user, attrs)
3303 {:ok, _} = ScheduledActivity.create(user, attrs)
3307 |> assign(:user, user)
3308 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3310 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3313 test "returns error when total user limit is exceeded", %{conn: conn} do
3314 user = insert(:user)
3317 NaiveDateTime.utc_now()
3318 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3319 |> NaiveDateTime.to_iso8601()
3322 NaiveDateTime.utc_now()
3323 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3324 |> NaiveDateTime.to_iso8601()
3326 attrs = %{params: %{}, scheduled_at: today}
3327 {:ok, _} = ScheduledActivity.create(user, attrs)
3328 {:ok, _} = ScheduledActivity.create(user, attrs)
3329 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3333 |> assign(:user, user)
3334 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3336 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3339 test "shows scheduled activities", %{conn: conn} do
3340 user = insert(:user)
3341 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3342 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3343 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3344 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3348 |> assign(:user, user)
3353 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3355 result = json_response(conn_res, 200)
3356 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3361 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3363 result = json_response(conn_res, 200)
3364 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3369 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3371 result = json_response(conn_res, 200)
3372 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3375 test "shows a scheduled activity", %{conn: conn} do
3376 user = insert(:user)
3377 scheduled_activity = insert(:scheduled_activity, user: user)
3381 |> assign(:user, user)
3382 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3384 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3385 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3389 |> assign(:user, user)
3390 |> get("/api/v1/scheduled_statuses/404")
3392 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3395 test "updates a scheduled activity", %{conn: conn} do
3396 user = insert(:user)
3397 scheduled_activity = insert(:scheduled_activity, user: user)
3400 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3404 |> assign(:user, user)
3405 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3406 scheduled_at: new_scheduled_at
3409 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3410 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3414 |> assign(:user, user)
3415 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3417 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3420 test "deletes a scheduled activity", %{conn: conn} do
3421 user = insert(:user)
3422 scheduled_activity = insert(:scheduled_activity, user: user)
3426 |> assign(:user, user)
3427 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3429 assert %{} = json_response(res_conn, 200)
3430 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3434 |> assign(:user, user)
3435 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3437 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3441 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3442 user1 = insert(:user)
3443 user2 = insert(:user)
3444 user3 = insert(:user)
3446 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3448 # Reply to status from another user
3451 |> assign(:user, user2)
3452 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3454 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3456 activity = Activity.get_by_id_with_object(id)
3458 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3459 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3461 # Reblog from the third user
3464 |> assign(:user, user3)
3465 |> post("/api/v1/statuses/#{activity.id}/reblog")
3467 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3468 json_response(conn2, 200)
3470 assert to_string(activity.id) == id
3472 # Getting third user status
3475 |> assign(:user, user3)
3476 |> get("api/v1/timelines/home")
3478 [reblogged_activity] = json_response(conn3, 200)
3480 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3482 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3483 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3486 describe "create account by app" do
3490 email: "lain@example.org",
3491 password: "PlzDontHackLain",
3495 [valid_params: valid_params]
3498 test "Account registration via Application", %{conn: conn} do
3501 |> post("/api/v1/apps", %{
3502 client_name: "client_name",
3503 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3504 scopes: "read, write, follow"
3508 "client_id" => client_id,
3509 "client_secret" => client_secret,
3511 "name" => "client_name",
3512 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3515 } = json_response(conn, 200)
3519 |> post("/oauth/token", %{
3520 grant_type: "client_credentials",
3521 client_id: client_id,
3522 client_secret: client_secret
3525 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3526 json_response(conn, 200)
3529 token_from_db = Repo.get_by(Token, token: token)
3530 assert token_from_db
3532 assert scope == "read write follow"
3536 |> put_req_header("authorization", "Bearer " <> token)
3537 |> post("/api/v1/accounts", %{
3539 email: "lain@example.org",
3540 password: "PlzDontHackLain",
3546 "access_token" => token,
3547 "created_at" => _created_at,
3549 "token_type" => "Bearer"
3550 } = json_response(conn, 200)
3552 token_from_db = Repo.get_by(Token, token: token)
3553 assert token_from_db
3554 token_from_db = Repo.preload(token_from_db, :user)
3555 assert token_from_db.user
3557 assert token_from_db.user.info.confirmation_pending
3560 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
3561 _user = insert(:user, email: "lain@example.org")
3562 app_token = insert(:oauth_token, user: nil)
3566 |> put_req_header("authorization", "Bearer " <> app_token.token)
3568 res = post(conn, "/api/v1/accounts", valid_params)
3569 assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
3572 test "rate limit", %{conn: conn} do
3573 app_token = insert(:oauth_token, user: nil)
3576 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3577 |> Map.put(:remote_ip, {15, 15, 15, 15})
3582 |> post("/api/v1/accounts", %{
3583 username: "#{i}lain",
3584 email: "#{i}lain@example.org",
3585 password: "PlzDontHackLain",
3590 "access_token" => token,
3591 "created_at" => _created_at,
3593 "token_type" => "Bearer"
3594 } = json_response(conn, 200)
3596 token_from_db = Repo.get_by(Token, token: token)
3597 assert token_from_db
3598 token_from_db = Repo.preload(token_from_db, :user)
3599 assert token_from_db.user
3601 assert token_from_db.user.info.confirmation_pending
3606 |> post("/api/v1/accounts", %{
3608 email: "6lain@example.org",
3609 password: "PlzDontHackLain",
3613 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3616 test "returns bad_request if missing required params", %{
3618 valid_params: valid_params
3620 app_token = insert(:oauth_token, user: nil)
3624 |> put_req_header("authorization", "Bearer " <> app_token.token)
3626 res = post(conn, "/api/v1/accounts", valid_params)
3627 assert json_response(res, 200)
3629 [{127,0,0,1}, {127,0,0,2}, {127,0,0,3}, {127,0,0,4}]
3630 |> Stream.zip(valid_params)
3631 |> Enum.each(fn {ip, {attr, _}} ->
3634 |> Map.put(:remote_ip, ip)
3635 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
3636 |> json_response(400)
3638 assert res == %{"error" => "Missing parameters"}
3642 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
3645 |> put_req_header("authorization", "Bearer " <> "invalid-token")
3647 res = post(conn, "/api/v1/accounts", valid_params)
3648 assert json_response(res, 403) == %{"error" => "Invalid credentials"}
3652 describe "GET /api/v1/polls/:id" do
3653 test "returns poll entity for object id", %{conn: conn} do
3654 user = insert(:user)
3657 CommonAPI.post(user, %{
3658 "status" => "Pleroma does",
3659 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3662 object = Object.normalize(activity)
3666 |> assign(:user, user)
3667 |> get("/api/v1/polls/#{object.id}")
3669 response = json_response(conn, 200)
3670 id = to_string(object.id)
3671 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3674 test "does not expose polls for private statuses", %{conn: conn} do
3675 user = insert(:user)
3676 other_user = insert(:user)
3679 CommonAPI.post(user, %{
3680 "status" => "Pleroma does",
3681 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3682 "visibility" => "private"
3685 object = Object.normalize(activity)
3689 |> assign(:user, other_user)
3690 |> get("/api/v1/polls/#{object.id}")
3692 assert json_response(conn, 404)
3696 describe "POST /api/v1/polls/:id/votes" do
3697 test "votes are added to the poll", %{conn: conn} do
3698 user = insert(:user)
3699 other_user = insert(:user)
3702 CommonAPI.post(user, %{
3703 "status" => "A very delicious sandwich",
3705 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3711 object = Object.normalize(activity)
3715 |> assign(:user, other_user)
3716 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3718 assert json_response(conn, 200)
3719 object = Object.get_by_id(object.id)
3721 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3726 test "author can't vote", %{conn: conn} do
3727 user = insert(:user)
3730 CommonAPI.post(user, %{
3731 "status" => "Am I cute?",
3732 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3735 object = Object.normalize(activity)
3738 |> assign(:user, user)
3739 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3740 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3742 object = Object.get_by_id(object.id)
3744 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3747 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3748 user = insert(:user)
3749 other_user = insert(:user)
3752 CommonAPI.post(user, %{
3753 "status" => "The glass is",
3754 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3757 object = Object.normalize(activity)
3760 |> assign(:user, other_user)
3761 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3762 |> json_response(422) == %{"error" => "Too many choices"}
3764 object = Object.get_by_id(object.id)
3766 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3771 test "does not allow choice index to be greater than options count", %{conn: conn} do
3772 user = insert(:user)
3773 other_user = insert(:user)
3776 CommonAPI.post(user, %{
3777 "status" => "Am I cute?",
3778 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3781 object = Object.normalize(activity)
3785 |> assign(:user, other_user)
3786 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3788 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3791 test "returns 404 error when object is not exist", %{conn: conn} do
3792 user = insert(:user)
3796 |> assign(:user, user)
3797 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3799 assert json_response(conn, 404) == %{"error" => "Record not found"}
3802 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3803 user = insert(:user)
3804 other_user = insert(:user)
3807 CommonAPI.post(user, %{
3808 "status" => "Am I cute?",
3809 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3810 "visibility" => "private"
3813 object = Object.normalize(activity)
3817 |> assign(:user, other_user)
3818 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3820 assert json_response(conn, 404) == %{"error" => "Record not found"}
3824 describe "GET /api/v1/statuses/:id/favourited_by" do
3826 user = insert(:user)
3827 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3831 |> assign(:user, user)
3833 [conn: conn, activity: activity]
3836 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3837 other_user = insert(:user)
3838 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3842 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3843 |> json_response(:ok)
3845 [%{"id" => id}] = response
3847 assert id == other_user.id
3850 test "returns empty array when status has not been favorited yet", %{
3856 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3857 |> json_response(:ok)
3859 assert Enum.empty?(response)
3862 test "does not return users who have favorited the status but are blocked", %{
3863 conn: %{assigns: %{user: user}} = conn,
3866 other_user = insert(:user)
3867 {:ok, user} = User.block(user, other_user)
3869 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3873 |> assign(:user, user)
3874 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3875 |> json_response(:ok)
3877 assert Enum.empty?(response)
3880 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3881 other_user = insert(:user)
3882 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3886 |> assign(:user, nil)
3887 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3888 |> json_response(:ok)
3890 [%{"id" => id}] = response
3891 assert id == other_user.id
3895 describe "GET /api/v1/statuses/:id/reblogged_by" do
3897 user = insert(:user)
3898 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3902 |> assign(:user, user)
3904 [conn: conn, activity: activity]
3907 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3908 other_user = insert(:user)
3909 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3913 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3914 |> json_response(:ok)
3916 [%{"id" => id}] = response
3918 assert id == other_user.id
3921 test "returns empty array when status has not been reblogged yet", %{
3927 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3928 |> json_response(:ok)
3930 assert Enum.empty?(response)
3933 test "does not return users who have reblogged the status but are blocked", %{
3934 conn: %{assigns: %{user: user}} = conn,
3937 other_user = insert(:user)
3938 {:ok, user} = User.block(user, other_user)
3940 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3944 |> assign(:user, user)
3945 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3946 |> json_response(:ok)
3948 assert Enum.empty?(response)
3951 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3952 other_user = insert(:user)
3953 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3957 |> assign(:user, nil)
3958 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3959 |> json_response(:ok)
3961 [%{"id" => id}] = response
3962 assert id == other_user.id
3966 describe "POST /auth/password, with valid parameters" do
3967 setup %{conn: conn} do
3968 user = insert(:user)
3969 conn = post(conn, "/auth/password?email=#{user.email}")
3970 %{conn: conn, user: user}
3973 test "it returns 204", %{conn: conn} do
3974 assert json_response(conn, :no_content)
3977 test "it creates a PasswordResetToken record for user", %{user: user} do
3978 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3982 test "it sends an email to user", %{user: user} do
3983 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3985 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3986 notify_email = Config.get([:instance, :notify_email])
3987 instance_name = Config.get([:instance, :name])
3990 from: {instance_name, notify_email},
3991 to: {user.name, user.email},
3992 html_body: email.html_body
3997 describe "POST /auth/password, with invalid parameters" do
3999 user = insert(:user)
4003 test "it returns 404 when user is not found", %{conn: conn, user: user} do
4004 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
4005 assert conn.status == 404
4006 assert conn.resp_body == ""
4009 test "it returns 400 when user is not local", %{conn: conn, user: user} do
4010 {:ok, user} = Repo.update(Changeset.change(user, local: false))
4011 conn = post(conn, "/auth/password?email=#{user.email}")
4012 assert conn.status == 400
4013 assert conn.resp_body == ""
4017 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
4019 user = insert(:user)
4020 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
4024 |> Changeset.change()
4025 |> Changeset.put_embed(:info, info_change)
4028 assert user.info.confirmation_pending
4033 clear_config([:instance, :account_activation_required]) do
4034 Config.put([:instance, :account_activation_required], true)
4037 test "resend account confirmation email", %{conn: conn, user: user} do
4039 |> assign(:user, user)
4040 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
4041 |> json_response(:no_content)
4043 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
4044 notify_email = Config.get([:instance, :notify_email])
4045 instance_name = Config.get([:instance, :name])
4048 from: {instance_name, notify_email},
4049 to: {user.name, user.email},
4050 html_body: email.html_body
4055 describe "GET /api/v1/suggestions" do
4057 user = insert(:user)
4058 other_user = insert(:user)
4059 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
4060 url500 = "http://test500?#{host}&#{user.nickname}"
4061 url200 = "http://test200?#{host}&#{user.nickname}"
4064 %{method: :get, url: ^url500} ->
4065 %Tesla.Env{status: 500, body: "bad request"}
4067 %{method: :get, url: ^url200} ->
4071 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4073 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4077 [user: user, other_user: other_user]
4080 clear_config(:suggestions)
4082 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4083 Config.put([:suggestions, :enabled], false)
4087 |> assign(:user, user)
4088 |> get("/api/v1/suggestions")
4089 |> json_response(200)
4094 test "returns error", %{conn: conn, user: user} do
4095 Config.put([:suggestions, :enabled], true)
4096 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4100 |> assign(:user, user)
4101 |> get("/api/v1/suggestions")
4102 |> json_response(500)
4104 assert res == "Something went wrong"
4107 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4108 Config.put([:suggestions, :enabled], true)
4109 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4113 |> assign(:user, user)
4114 |> get("/api/v1/suggestions")
4115 |> json_response(200)
4120 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4121 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4125 "acct" => other_user.ap_id,
4126 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4127 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4128 "id" => other_user.id
4134 describe "PUT /api/v1/media/:id" do
4136 actor = insert(:user)
4138 file = %Plug.Upload{
4139 content_type: "image/jpg",
4140 path: Path.absname("test/fixtures/image.jpg"),
4141 filename: "an_image.jpg"
4144 {:ok, %Object{} = object} =
4147 actor: User.ap_id(actor),
4148 description: "test-m"
4151 [actor: actor, object: object]
4154 test "updates name of media", %{conn: conn, actor: actor, object: object} do
4157 |> assign(:user, actor)
4158 |> put("/api/v1/media/#{object.id}", %{"description" => "test-media"})
4159 |> json_response(:ok)
4161 assert media["description"] == "test-media"
4162 assert refresh_record(object).data["name"] == "test-media"
4165 test "returns error wheb request is bad", %{conn: conn, actor: actor, object: object} do
4168 |> assign(:user, actor)
4169 |> put("/api/v1/media/#{object.id}", %{})
4170 |> json_response(400)
4172 assert media == %{"error" => "bad_request"}
4176 describe "DELETE /auth/sign_out" do
4177 test "redirect to root page", %{conn: conn} do
4178 user = insert(:user)
4182 |> assign(:user, user)
4183 |> delete("/auth/sign_out")
4185 assert conn.status == 302
4186 assert redirected_to(conn) == "/"
4190 describe "GET /api/v1/accounts/:id/lists - account_lists" do
4191 test "returns lists to which the account belongs", %{conn: conn} do
4192 user = insert(:user)
4193 other_user = insert(:user)
4194 assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user)
4195 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
4199 |> assign(:user, user)
4200 |> get("/api/v1/accounts/#{other_user.id}/lists")
4201 |> json_response(200)
4203 assert res == [%{"id" => to_string(list.id), "title" => "Test List"}]
4207 describe "empty_array, stubs for mastodon api" do
4208 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
4209 user = insert(:user)
4213 |> assign(:user, user)
4214 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
4215 |> json_response(200)
4220 test "GET /api/v1/endorsements", %{conn: conn} do
4221 user = insert(:user)
4225 |> assign(:user, user)
4226 |> get("/api/v1/endorsements")
4227 |> json_response(200)
4232 test "GET /api/v1/trends", %{conn: conn} do
4233 user = insert(:user)
4237 |> assign(:user, user)
4238 |> get("/api/v1/trends")
4239 |> json_response(200)