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 alias Pleroma.Web.TwitterAPI.TwitterAPI
25 import Pleroma.Factory
26 import ExUnit.CaptureLog
28 import Swoosh.TestAssertions
30 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
33 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
37 clear_config([:instance, :public])
38 clear_config([:rich_media, :enabled])
40 test "the home timeline", %{conn: conn} do
42 following = insert(:user)
44 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
51 assert Enum.empty?(json_response(conn, 200))
53 {:ok, user} = User.follow(user, following)
57 |> assign(:user, user)
58 |> get("/api/v1/timelines/home")
60 assert [%{"content" => "test"}] = json_response(conn, 200)
63 test "the public timeline", %{conn: conn} do
64 following = insert(:user)
67 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
70 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
74 |> get("/api/v1/timelines/public", %{"local" => "False"})
76 assert length(json_response(conn, 200)) == 2
80 |> get("/api/v1/timelines/public", %{"local" => "True"})
82 assert [%{"content" => "test"}] = json_response(conn, 200)
86 |> get("/api/v1/timelines/public", %{"local" => "1"})
88 assert [%{"content" => "test"}] = json_response(conn, 200)
92 test "the public timeline when public is set to false", %{conn: conn} do
93 Config.put([:instance, :public], false)
96 |> get("/api/v1/timelines/public", %{"local" => "False"})
97 |> json_response(403) == %{"error" => "This resource requires authentication."}
100 describe "posting statuses" do
106 |> assign(:user, user)
111 test "posting a status", %{conn: conn} do
112 idempotency_key = "Pikachu rocks!"
116 |> put_req_header("idempotency-key", idempotency_key)
117 |> post("/api/v1/statuses", %{
119 "spoiler_text" => "2hu",
120 "sensitive" => "false"
123 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
125 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
127 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
128 json_response(conn_one, 200)
130 assert Activity.get_by_id(id)
134 |> put_req_header("idempotency-key", idempotency_key)
135 |> post("/api/v1/statuses", %{
137 "spoiler_text" => "2hu",
138 "sensitive" => "false"
141 assert %{"id" => second_id} = json_response(conn_two, 200)
142 assert id == second_id
146 |> post("/api/v1/statuses", %{
148 "spoiler_text" => "2hu",
149 "sensitive" => "false"
152 assert %{"id" => third_id} = json_response(conn_three, 200)
153 refute id == third_id
155 # An activity that will expire:
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(:timer.minutes(expires_in), :millisecond)
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
176 assert fourth_response["pleroma"]["expires_at"] == NaiveDateTime.to_iso8601(expiration.scheduled_at)
179 test "replying to a status", %{conn: conn} do
181 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
185 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
187 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
189 activity = Activity.get_by_id(id)
191 assert activity.data["context"] == replied_to.data["context"]
192 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
195 test "replying to a direct message with visibility other than direct", %{conn: conn} do
197 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
199 Enum.each(["public", "private", "unlisted"], fn visibility ->
202 |> post("/api/v1/statuses", %{
203 "status" => "@#{user.nickname} hey",
204 "in_reply_to_id" => replied_to.id,
205 "visibility" => visibility
208 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
212 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
215 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
217 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
218 assert Activity.get_by_id(id)
221 test "posting a sensitive status", %{conn: conn} do
224 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
226 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
227 assert Activity.get_by_id(id)
230 test "posting a fake status", %{conn: conn} do
233 |> post("/api/v1/statuses", %{
235 "\"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"
238 real_status = json_response(real_conn, 200)
241 assert Object.get_by_ap_id(real_status["uri"])
245 |> Map.put("id", nil)
246 |> Map.put("url", nil)
247 |> Map.put("uri", nil)
248 |> Map.put("created_at", nil)
249 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
253 |> post("/api/v1/statuses", %{
255 "\"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",
259 fake_status = json_response(fake_conn, 200)
262 refute Object.get_by_ap_id(fake_status["uri"])
266 |> Map.put("id", nil)
267 |> Map.put("url", nil)
268 |> Map.put("uri", nil)
269 |> Map.put("created_at", nil)
270 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
272 assert real_status == fake_status
275 test "posting a status with OGP link preview", %{conn: conn} do
276 Config.put([:rich_media, :enabled], true)
280 |> post("/api/v1/statuses", %{
281 "status" => "https://example.com/ogp"
284 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
285 assert Activity.get_by_id(id)
288 test "posting a direct status", %{conn: conn} do
289 user2 = insert(:user)
290 content = "direct cofe @#{user2.nickname}"
294 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
296 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
297 assert activity = Activity.get_by_id(id)
298 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
299 assert activity.data["to"] == [user2.ap_id]
300 assert activity.data["cc"] == []
304 describe "posting polls" do
305 test "posting a poll", %{conn: conn} do
307 time = NaiveDateTime.utc_now()
311 |> assign(:user, user)
312 |> post("/api/v1/statuses", %{
313 "status" => "Who is the #bestgrill?",
314 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
317 response = json_response(conn, 200)
319 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
320 title in ["Rei", "Asuka", "Misato"]
323 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
324 refute response["poll"]["expred"]
327 test "option limit is enforced", %{conn: conn} do
329 limit = Config.get([:instance, :poll_limits, :max_options])
333 |> assign(:user, user)
334 |> post("/api/v1/statuses", %{
336 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
339 %{"error" => error} = json_response(conn, 422)
340 assert error == "Poll can't contain more than #{limit} options"
343 test "option character limit is enforced", %{conn: conn} do
345 limit = Config.get([:instance, :poll_limits, :max_option_chars])
349 |> assign(:user, user)
350 |> post("/api/v1/statuses", %{
353 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
358 %{"error" => error} = json_response(conn, 422)
359 assert error == "Poll options cannot be longer than #{limit} characters each"
362 test "minimal date limit is enforced", %{conn: conn} do
364 limit = Config.get([:instance, :poll_limits, :min_expiration])
368 |> assign(:user, user)
369 |> post("/api/v1/statuses", %{
370 "status" => "imagine arbitrary limits",
372 "options" => ["this post was made by pleroma gang"],
373 "expires_in" => limit - 1
377 %{"error" => error} = json_response(conn, 422)
378 assert error == "Expiration date is too soon"
381 test "maximum date limit is enforced", %{conn: conn} do
383 limit = Config.get([:instance, :poll_limits, :max_expiration])
387 |> assign(:user, user)
388 |> post("/api/v1/statuses", %{
389 "status" => "imagine arbitrary limits",
391 "options" => ["this post was made by pleroma gang"],
392 "expires_in" => limit + 1
396 %{"error" => error} = json_response(conn, 422)
397 assert error == "Expiration date is too far in the future"
401 test "direct timeline", %{conn: conn} do
402 user_one = insert(:user)
403 user_two = insert(:user)
405 {:ok, user_two} = User.follow(user_two, user_one)
408 CommonAPI.post(user_one, %{
409 "status" => "Hi @#{user_two.nickname}!",
410 "visibility" => "direct"
413 {:ok, _follower_only} =
414 CommonAPI.post(user_one, %{
415 "status" => "Hi @#{user_two.nickname}!",
416 "visibility" => "private"
419 # Only direct should be visible here
422 |> assign(:user, user_two)
423 |> get("api/v1/timelines/direct")
425 [status] = json_response(res_conn, 200)
427 assert %{"visibility" => "direct"} = status
428 assert status["url"] != direct.data["id"]
430 # User should be able to see their own direct message
433 |> assign(:user, user_one)
434 |> get("api/v1/timelines/direct")
436 [status] = json_response(res_conn, 200)
438 assert %{"visibility" => "direct"} = status
440 # Both should be visible here
443 |> assign(:user, user_two)
444 |> get("api/v1/timelines/home")
446 [_s1, _s2] = json_response(res_conn, 200)
449 Enum.each(1..20, fn _ ->
451 CommonAPI.post(user_one, %{
452 "status" => "Hi @#{user_two.nickname}!",
453 "visibility" => "direct"
459 |> assign(:user, user_two)
460 |> get("api/v1/timelines/direct")
462 statuses = json_response(res_conn, 200)
463 assert length(statuses) == 20
467 |> assign(:user, user_two)
468 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
470 [status] = json_response(res_conn, 200)
472 assert status["url"] != direct.data["id"]
475 test "Conversations", %{conn: conn} do
476 user_one = insert(:user)
477 user_two = insert(:user)
478 user_three = insert(:user)
480 {:ok, user_two} = User.follow(user_two, user_one)
483 CommonAPI.post(user_one, %{
484 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
485 "visibility" => "direct"
488 {:ok, _follower_only} =
489 CommonAPI.post(user_one, %{
490 "status" => "Hi @#{user_two.nickname}!",
491 "visibility" => "private"
496 |> assign(:user, user_one)
497 |> get("/api/v1/conversations")
499 assert response = json_response(res_conn, 200)
504 "accounts" => res_accounts,
505 "last_status" => res_last_status,
510 account_ids = Enum.map(res_accounts, & &1["id"])
511 assert length(res_accounts) == 2
512 assert user_two.id in account_ids
513 assert user_three.id in account_ids
514 assert is_binary(res_id)
515 assert unread == true
516 assert res_last_status["id"] == direct.id
518 # Apparently undocumented API endpoint
521 |> assign(:user, user_one)
522 |> post("/api/v1/conversations/#{res_id}/read")
524 assert response = json_response(res_conn, 200)
525 assert length(response["accounts"]) == 2
526 assert response["last_status"]["id"] == direct.id
527 assert response["unread"] == false
529 # (vanilla) Mastodon frontend behaviour
532 |> assign(:user, user_one)
533 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
535 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
538 test "doesn't include DMs from blocked users", %{conn: conn} do
539 blocker = insert(:user)
540 blocked = insert(:user)
542 {:ok, blocker} = User.block(blocker, blocked)
544 {:ok, _blocked_direct} =
545 CommonAPI.post(blocked, %{
546 "status" => "Hi @#{blocker.nickname}!",
547 "visibility" => "direct"
551 CommonAPI.post(user, %{
552 "status" => "Hi @#{blocker.nickname}!",
553 "visibility" => "direct"
558 |> assign(:user, user)
559 |> get("api/v1/timelines/direct")
561 [status] = json_response(res_conn, 200)
562 assert status["id"] == direct.id
565 test "verify_credentials", %{conn: conn} do
570 |> assign(:user, user)
571 |> get("/api/v1/accounts/verify_credentials")
573 response = json_response(conn, 200)
575 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
576 assert response["pleroma"]["chat_token"]
577 assert id == to_string(user.id)
580 test "verify_credentials default scope unlisted", %{conn: conn} do
581 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
585 |> assign(:user, user)
586 |> get("/api/v1/accounts/verify_credentials")
588 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
589 assert id == to_string(user.id)
592 test "apps/verify_credentials", %{conn: conn} do
593 token = insert(:oauth_token)
597 |> assign(:user, token.user)
598 |> assign(:token, token)
599 |> get("/api/v1/apps/verify_credentials")
601 app = Repo.preload(token, :app).app
604 "name" => app.client_name,
605 "website" => app.website,
606 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
609 assert expected == json_response(conn, 200)
612 test "user avatar can be set", %{conn: conn} do
614 avatar_image = File.read!("test/fixtures/avatar_data_uri")
618 |> assign(:user, user)
619 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
621 user = refresh_record(user)
635 assert %{"url" => _} = json_response(conn, 200)
638 test "user avatar can be reset", %{conn: conn} do
643 |> assign(:user, user)
644 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
646 user = User.get_cached_by_id(user.id)
648 assert user.avatar == nil
650 assert %{"url" => nil} = json_response(conn, 200)
653 test "can set profile banner", %{conn: conn} do
658 |> assign(:user, user)
659 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
661 user = refresh_record(user)
662 assert user.info.banner["type"] == "Image"
664 assert %{"url" => _} = json_response(conn, 200)
667 test "can reset profile banner", %{conn: conn} do
672 |> assign(:user, user)
673 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
675 user = refresh_record(user)
676 assert user.info.banner == %{}
678 assert %{"url" => nil} = json_response(conn, 200)
681 test "background image can be set", %{conn: conn} do
686 |> assign(:user, user)
687 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
689 user = refresh_record(user)
690 assert user.info.background["type"] == "Image"
691 assert %{"url" => _} = json_response(conn, 200)
694 test "background image can be reset", %{conn: conn} do
699 |> assign(:user, user)
700 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
702 user = refresh_record(user)
703 assert user.info.background == %{}
704 assert %{"url" => nil} = json_response(conn, 200)
707 test "creates an oauth app", %{conn: conn} do
709 app_attrs = build(:oauth_app)
713 |> assign(:user, user)
714 |> post("/api/v1/apps", %{
715 client_name: app_attrs.client_name,
716 redirect_uris: app_attrs.redirect_uris
719 [app] = Repo.all(App)
722 "name" => app.client_name,
723 "website" => app.website,
724 "client_id" => app.client_id,
725 "client_secret" => app.client_secret,
726 "id" => app.id |> to_string(),
727 "redirect_uri" => app.redirect_uris,
728 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
731 assert expected == json_response(conn, 200)
734 test "get a status", %{conn: conn} do
735 activity = insert(:note_activity)
739 |> get("/api/v1/statuses/#{activity.id}")
741 assert %{"id" => id} = json_response(conn, 200)
742 assert id == to_string(activity.id)
745 describe "deleting a status" do
746 test "when you created it", %{conn: conn} do
747 activity = insert(:note_activity)
748 author = User.get_cached_by_ap_id(activity.data["actor"])
752 |> assign(:user, author)
753 |> delete("/api/v1/statuses/#{activity.id}")
755 assert %{} = json_response(conn, 200)
757 refute Activity.get_by_id(activity.id)
760 test "when you didn't create it", %{conn: conn} do
761 activity = insert(:note_activity)
766 |> assign(:user, user)
767 |> delete("/api/v1/statuses/#{activity.id}")
769 assert %{"error" => _} = json_response(conn, 403)
771 assert Activity.get_by_id(activity.id) == activity
774 test "when you're an admin or moderator", %{conn: conn} do
775 activity1 = insert(:note_activity)
776 activity2 = insert(:note_activity)
777 admin = insert(:user, info: %{is_admin: true})
778 moderator = insert(:user, info: %{is_moderator: true})
782 |> assign(:user, admin)
783 |> delete("/api/v1/statuses/#{activity1.id}")
785 assert %{} = json_response(res_conn, 200)
789 |> assign(:user, moderator)
790 |> delete("/api/v1/statuses/#{activity2.id}")
792 assert %{} = json_response(res_conn, 200)
794 refute Activity.get_by_id(activity1.id)
795 refute Activity.get_by_id(activity2.id)
799 describe "filters" do
800 test "creating a filter", %{conn: conn} do
803 filter = %Pleroma.Filter{
810 |> assign(:user, user)
811 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
813 assert response = json_response(conn, 200)
814 assert response["phrase"] == filter.phrase
815 assert response["context"] == filter.context
816 assert response["irreversible"] == false
817 assert response["id"] != nil
818 assert response["id"] != ""
821 test "fetching a list of filters", %{conn: conn} do
824 query_one = %Pleroma.Filter{
831 query_two = %Pleroma.Filter{
838 {:ok, filter_one} = Pleroma.Filter.create(query_one)
839 {:ok, filter_two} = Pleroma.Filter.create(query_two)
843 |> assign(:user, user)
844 |> get("/api/v1/filters")
845 |> json_response(200)
851 filters: [filter_two, filter_one]
855 test "get a filter", %{conn: conn} do
858 query = %Pleroma.Filter{
865 {:ok, filter} = Pleroma.Filter.create(query)
869 |> assign(:user, user)
870 |> get("/api/v1/filters/#{filter.filter_id}")
872 assert _response = json_response(conn, 200)
875 test "update a filter", %{conn: conn} do
878 query = %Pleroma.Filter{
885 {:ok, _filter} = Pleroma.Filter.create(query)
887 new = %Pleroma.Filter{
894 |> assign(:user, user)
895 |> put("/api/v1/filters/#{query.filter_id}", %{
900 assert response = json_response(conn, 200)
901 assert response["phrase"] == new.phrase
902 assert response["context"] == new.context
905 test "delete a filter", %{conn: conn} do
908 query = %Pleroma.Filter{
915 {:ok, filter} = Pleroma.Filter.create(query)
919 |> assign(:user, user)
920 |> delete("/api/v1/filters/#{filter.filter_id}")
922 assert response = json_response(conn, 200)
923 assert response == %{}
928 test "creating a list", %{conn: conn} do
933 |> assign(:user, user)
934 |> post("/api/v1/lists", %{"title" => "cuties"})
936 assert %{"title" => title} = json_response(conn, 200)
937 assert title == "cuties"
940 test "adding users to a list", %{conn: conn} do
942 other_user = insert(:user)
943 {:ok, list} = Pleroma.List.create("name", user)
947 |> assign(:user, user)
948 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
950 assert %{} == json_response(conn, 200)
951 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
952 assert following == [other_user.follower_address]
955 test "removing users from a list", %{conn: conn} do
957 other_user = insert(:user)
958 third_user = insert(:user)
959 {:ok, list} = Pleroma.List.create("name", user)
960 {:ok, list} = Pleroma.List.follow(list, other_user)
961 {:ok, list} = Pleroma.List.follow(list, third_user)
965 |> assign(:user, user)
966 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
968 assert %{} == json_response(conn, 200)
969 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
970 assert following == [third_user.follower_address]
973 test "listing users in a list", %{conn: conn} do
975 other_user = insert(:user)
976 {:ok, list} = Pleroma.List.create("name", user)
977 {:ok, list} = Pleroma.List.follow(list, other_user)
981 |> assign(:user, user)
982 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
984 assert [%{"id" => id}] = json_response(conn, 200)
985 assert id == to_string(other_user.id)
988 test "retrieving a list", %{conn: conn} do
990 {:ok, list} = Pleroma.List.create("name", user)
994 |> assign(:user, user)
995 |> get("/api/v1/lists/#{list.id}")
997 assert %{"id" => id} = json_response(conn, 200)
998 assert id == to_string(list.id)
1001 test "renaming a list", %{conn: conn} do
1002 user = insert(:user)
1003 {:ok, list} = Pleroma.List.create("name", user)
1007 |> assign(:user, user)
1008 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
1010 assert %{"title" => name} = json_response(conn, 200)
1011 assert name == "newname"
1014 test "deleting a list", %{conn: conn} do
1015 user = insert(:user)
1016 {:ok, list} = Pleroma.List.create("name", user)
1020 |> assign(:user, user)
1021 |> delete("/api/v1/lists/#{list.id}")
1023 assert %{} = json_response(conn, 200)
1024 assert is_nil(Repo.get(Pleroma.List, list.id))
1027 test "list timeline", %{conn: conn} do
1028 user = insert(:user)
1029 other_user = insert(:user)
1030 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
1031 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1032 {:ok, list} = Pleroma.List.create("name", user)
1033 {:ok, list} = Pleroma.List.follow(list, other_user)
1037 |> assign(:user, user)
1038 |> get("/api/v1/timelines/list/#{list.id}")
1040 assert [%{"id" => id}] = json_response(conn, 200)
1042 assert id == to_string(activity_two.id)
1045 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1046 user = insert(:user)
1047 other_user = insert(:user)
1048 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1050 {:ok, _activity_two} =
1051 CommonAPI.post(other_user, %{
1052 "status" => "Marisa is cute.",
1053 "visibility" => "private"
1056 {:ok, list} = Pleroma.List.create("name", user)
1057 {:ok, list} = Pleroma.List.follow(list, other_user)
1061 |> assign(:user, user)
1062 |> get("/api/v1/timelines/list/#{list.id}")
1064 assert [%{"id" => id}] = json_response(conn, 200)
1066 assert id == to_string(activity_one.id)
1070 describe "notifications" do
1071 test "list of notifications", %{conn: conn} do
1072 user = insert(:user)
1073 other_user = insert(:user)
1075 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1077 {:ok, [_notification]} = Notification.create_notifications(activity)
1081 |> assign(:user, user)
1082 |> get("/api/v1/notifications")
1085 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1087 }\">@<span>#{user.nickname}</span></a></span>"
1089 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1090 assert response == expected_response
1093 test "getting a single notification", %{conn: conn} do
1094 user = insert(:user)
1095 other_user = insert(:user)
1097 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1099 {:ok, [notification]} = Notification.create_notifications(activity)
1103 |> assign(:user, user)
1104 |> get("/api/v1/notifications/#{notification.id}")
1107 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1109 }\">@<span>#{user.nickname}</span></a></span>"
1111 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1112 assert response == expected_response
1115 test "dismissing a single notification", %{conn: conn} do
1116 user = insert(:user)
1117 other_user = insert(:user)
1119 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1121 {:ok, [notification]} = Notification.create_notifications(activity)
1125 |> assign(:user, user)
1126 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1128 assert %{} = json_response(conn, 200)
1131 test "clearing all notifications", %{conn: conn} do
1132 user = insert(:user)
1133 other_user = insert(:user)
1135 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1137 {:ok, [_notification]} = Notification.create_notifications(activity)
1141 |> assign(:user, user)
1142 |> post("/api/v1/notifications/clear")
1144 assert %{} = json_response(conn, 200)
1148 |> assign(:user, user)
1149 |> get("/api/v1/notifications")
1151 assert all = json_response(conn, 200)
1155 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1156 user = insert(:user)
1157 other_user = insert(:user)
1159 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1160 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1161 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1162 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1164 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1165 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1166 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1167 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1171 |> assign(:user, user)
1176 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1178 result = json_response(conn_res, 200)
1179 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1184 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1186 result = json_response(conn_res, 200)
1187 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1192 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1194 result = json_response(conn_res, 200)
1195 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1198 test "filters notifications using exclude_types", %{conn: conn} do
1199 user = insert(:user)
1200 other_user = insert(:user)
1202 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1203 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1204 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1205 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1206 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1208 mention_notification_id =
1209 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1211 favorite_notification_id =
1212 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1214 reblog_notification_id =
1215 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1217 follow_notification_id =
1218 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1222 |> assign(:user, user)
1225 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1227 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1230 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1232 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1235 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1237 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1240 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1242 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1245 test "destroy multiple", %{conn: conn} do
1246 user = insert(:user)
1247 other_user = insert(:user)
1249 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1250 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1251 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1252 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1254 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1255 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1256 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1257 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1261 |> assign(:user, user)
1265 |> get("/api/v1/notifications")
1267 result = json_response(conn_res, 200)
1268 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1272 |> assign(:user, other_user)
1276 |> get("/api/v1/notifications")
1278 result = json_response(conn_res, 200)
1279 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1283 |> delete("/api/v1/notifications/destroy_multiple", %{
1284 "ids" => [notification1_id, notification2_id]
1287 assert json_response(conn_destroy, 200) == %{}
1291 |> get("/api/v1/notifications")
1293 result = json_response(conn_res, 200)
1294 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1297 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1298 user = insert(:user)
1299 user2 = insert(:user)
1301 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1302 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1304 conn = assign(conn, :user, user)
1306 conn = get(conn, "/api/v1/notifications")
1308 assert length(json_response(conn, 200)) == 1
1310 {:ok, user} = User.mute(user, user2)
1312 conn = assign(build_conn(), :user, user)
1313 conn = get(conn, "/api/v1/notifications")
1315 assert json_response(conn, 200) == []
1318 test "see notifications after muting user without notifications", %{conn: conn} do
1319 user = insert(:user)
1320 user2 = insert(:user)
1322 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1323 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1325 conn = assign(conn, :user, user)
1327 conn = get(conn, "/api/v1/notifications")
1329 assert length(json_response(conn, 200)) == 1
1331 {:ok, user} = User.mute(user, user2, false)
1333 conn = assign(build_conn(), :user, user)
1334 conn = get(conn, "/api/v1/notifications")
1336 assert length(json_response(conn, 200)) == 1
1339 test "see notifications after muting user with notifications and with_muted parameter", %{
1342 user = insert(:user)
1343 user2 = insert(:user)
1345 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1346 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1348 conn = assign(conn, :user, user)
1350 conn = get(conn, "/api/v1/notifications")
1352 assert length(json_response(conn, 200)) == 1
1354 {:ok, user} = User.mute(user, user2)
1356 conn = assign(build_conn(), :user, user)
1357 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1359 assert length(json_response(conn, 200)) == 1
1363 describe "reblogging" do
1364 test "reblogs and returns the reblogged status", %{conn: conn} do
1365 activity = insert(:note_activity)
1366 user = insert(:user)
1370 |> assign(:user, user)
1371 |> post("/api/v1/statuses/#{activity.id}/reblog")
1374 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1376 } = json_response(conn, 200)
1378 assert to_string(activity.id) == id
1381 test "reblogged status for another user", %{conn: conn} do
1382 activity = insert(:note_activity)
1383 user1 = insert(:user)
1384 user2 = insert(:user)
1385 user3 = insert(:user)
1386 CommonAPI.favorite(activity.id, user2)
1387 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1388 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1389 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1393 |> assign(:user, user3)
1394 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1397 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1398 "reblogged" => false,
1399 "favourited" => false,
1400 "bookmarked" => false
1401 } = json_response(conn_res, 200)
1405 |> assign(:user, user2)
1406 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1409 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1410 "reblogged" => true,
1411 "favourited" => true,
1412 "bookmarked" => true
1413 } = json_response(conn_res, 200)
1415 assert to_string(activity.id) == id
1418 test "returns 400 error when activity is not exist", %{conn: conn} do
1419 user = insert(:user)
1423 |> assign(:user, user)
1424 |> post("/api/v1/statuses/foo/reblog")
1426 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1430 describe "unreblogging" do
1431 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1432 activity = insert(:note_activity)
1433 user = insert(:user)
1435 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1439 |> assign(:user, user)
1440 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1442 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1444 assert to_string(activity.id) == id
1447 test "returns 400 error when activity is not exist", %{conn: conn} do
1448 user = insert(:user)
1452 |> assign(:user, user)
1453 |> post("/api/v1/statuses/foo/unreblog")
1455 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1459 describe "favoriting" do
1460 test "favs a status and returns it", %{conn: conn} do
1461 activity = insert(:note_activity)
1462 user = insert(:user)
1466 |> assign(:user, user)
1467 |> post("/api/v1/statuses/#{activity.id}/favourite")
1469 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1470 json_response(conn, 200)
1472 assert to_string(activity.id) == id
1475 test "returns 400 error for a wrong id", %{conn: conn} do
1476 user = insert(:user)
1480 |> assign(:user, user)
1481 |> post("/api/v1/statuses/1/favourite")
1483 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1487 describe "unfavoriting" do
1488 test "unfavorites a status and returns it", %{conn: conn} do
1489 activity = insert(:note_activity)
1490 user = insert(:user)
1492 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1496 |> assign(:user, user)
1497 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1499 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1500 json_response(conn, 200)
1502 assert to_string(activity.id) == id
1505 test "returns 400 error for a wrong id", %{conn: conn} do
1506 user = insert(:user)
1510 |> assign(:user, user)
1511 |> post("/api/v1/statuses/1/unfavourite")
1513 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1517 describe "user timelines" do
1518 test "gets a users statuses", %{conn: conn} do
1519 user_one = insert(:user)
1520 user_two = insert(:user)
1521 user_three = insert(:user)
1523 {:ok, user_three} = User.follow(user_three, user_one)
1525 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1527 {:ok, direct_activity} =
1528 CommonAPI.post(user_one, %{
1529 "status" => "Hi, @#{user_two.nickname}.",
1530 "visibility" => "direct"
1533 {:ok, private_activity} =
1534 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1538 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1540 assert [%{"id" => id}] = json_response(resp, 200)
1541 assert id == to_string(activity.id)
1545 |> assign(:user, user_two)
1546 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1548 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1549 assert id_one == to_string(direct_activity.id)
1550 assert id_two == to_string(activity.id)
1554 |> assign(:user, user_three)
1555 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1557 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1558 assert id_one == to_string(private_activity.id)
1559 assert id_two == to_string(activity.id)
1562 test "unimplemented pinned statuses feature", %{conn: conn} do
1563 note = insert(:note_activity)
1564 user = User.get_cached_by_ap_id(note.data["actor"])
1568 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1570 assert json_response(conn, 200) == []
1573 test "gets an users media", %{conn: conn} do
1574 note = insert(:note_activity)
1575 user = User.get_cached_by_ap_id(note.data["actor"])
1577 file = %Plug.Upload{
1578 content_type: "image/jpg",
1579 path: Path.absname("test/fixtures/image.jpg"),
1580 filename: "an_image.jpg"
1584 TwitterAPI.upload(file, user, "json")
1588 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1592 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1594 assert [%{"id" => id}] = json_response(conn, 200)
1595 assert id == to_string(image_post.id)
1599 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1601 assert [%{"id" => id}] = json_response(conn, 200)
1602 assert id == to_string(image_post.id)
1605 test "gets a user's statuses without reblogs", %{conn: conn} do
1606 user = insert(:user)
1607 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1608 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1612 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1614 assert [%{"id" => id}] = json_response(conn, 200)
1615 assert id == to_string(post.id)
1619 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1621 assert [%{"id" => id}] = json_response(conn, 200)
1622 assert id == to_string(post.id)
1625 test "filters user's statuses by a hashtag", %{conn: conn} do
1626 user = insert(:user)
1627 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1628 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1632 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1634 assert [%{"id" => id}] = json_response(conn, 200)
1635 assert id == to_string(post.id)
1639 describe "user relationships" do
1640 test "returns the relationships for the current user", %{conn: conn} do
1641 user = insert(:user)
1642 other_user = insert(:user)
1643 {:ok, user} = User.follow(user, other_user)
1647 |> assign(:user, user)
1648 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1650 assert [relationship] = json_response(conn, 200)
1652 assert to_string(other_user.id) == relationship["id"]
1656 describe "media upload" do
1658 user = insert(:user)
1662 |> assign(:user, user)
1664 image = %Plug.Upload{
1665 content_type: "image/jpg",
1666 path: Path.absname("test/fixtures/image.jpg"),
1667 filename: "an_image.jpg"
1670 [conn: conn, image: image]
1673 clear_config([:media_proxy])
1674 clear_config([Pleroma.Upload])
1676 test "returns uploaded image", %{conn: conn, image: image} do
1677 desc = "Description of the image"
1681 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1682 |> json_response(:ok)
1684 assert media["type"] == "image"
1685 assert media["description"] == desc
1688 object = Repo.get(Object, media["id"])
1689 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1693 describe "locked accounts" do
1694 test "/api/v1/follow_requests works" do
1695 user = insert(:user, %{info: %User.Info{locked: true}})
1696 other_user = insert(:user)
1698 {:ok, _activity} = ActivityPub.follow(other_user, user)
1700 user = User.get_cached_by_id(user.id)
1701 other_user = User.get_cached_by_id(other_user.id)
1703 assert User.following?(other_user, user) == false
1707 |> assign(:user, user)
1708 |> get("/api/v1/follow_requests")
1710 assert [relationship] = json_response(conn, 200)
1711 assert to_string(other_user.id) == relationship["id"]
1714 test "/api/v1/follow_requests/:id/authorize works" do
1715 user = insert(:user, %{info: %User.Info{locked: true}})
1716 other_user = insert(:user)
1718 {:ok, _activity} = ActivityPub.follow(other_user, user)
1720 user = User.get_cached_by_id(user.id)
1721 other_user = User.get_cached_by_id(other_user.id)
1723 assert User.following?(other_user, user) == false
1727 |> assign(:user, user)
1728 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1730 assert relationship = json_response(conn, 200)
1731 assert to_string(other_user.id) == relationship["id"]
1733 user = User.get_cached_by_id(user.id)
1734 other_user = User.get_cached_by_id(other_user.id)
1736 assert User.following?(other_user, user) == true
1739 test "verify_credentials", %{conn: conn} do
1740 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1744 |> assign(:user, user)
1745 |> get("/api/v1/accounts/verify_credentials")
1747 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1748 assert id == to_string(user.id)
1751 test "/api/v1/follow_requests/:id/reject works" do
1752 user = insert(:user, %{info: %User.Info{locked: true}})
1753 other_user = insert(:user)
1755 {:ok, _activity} = ActivityPub.follow(other_user, user)
1757 user = User.get_cached_by_id(user.id)
1761 |> assign(:user, user)
1762 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1764 assert relationship = json_response(conn, 200)
1765 assert to_string(other_user.id) == relationship["id"]
1767 user = User.get_cached_by_id(user.id)
1768 other_user = User.get_cached_by_id(other_user.id)
1770 assert User.following?(other_user, user) == false
1774 test "account fetching", %{conn: conn} do
1775 user = insert(:user)
1779 |> get("/api/v1/accounts/#{user.id}")
1781 assert %{"id" => id} = json_response(conn, 200)
1782 assert id == to_string(user.id)
1786 |> get("/api/v1/accounts/-1")
1788 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1791 test "account fetching also works nickname", %{conn: conn} do
1792 user = insert(:user)
1796 |> get("/api/v1/accounts/#{user.nickname}")
1798 assert %{"id" => id} = json_response(conn, 200)
1799 assert id == user.id
1802 test "mascot upload", %{conn: conn} do
1803 user = insert(:user)
1805 non_image_file = %Plug.Upload{
1806 content_type: "audio/mpeg",
1807 path: Path.absname("test/fixtures/sound.mp3"),
1808 filename: "sound.mp3"
1813 |> assign(:user, user)
1814 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1816 assert json_response(conn, 415)
1818 file = %Plug.Upload{
1819 content_type: "image/jpg",
1820 path: Path.absname("test/fixtures/image.jpg"),
1821 filename: "an_image.jpg"
1826 |> assign(:user, user)
1827 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1829 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1832 test "mascot retrieving", %{conn: conn} do
1833 user = insert(:user)
1834 # When user hasn't set a mascot, we should just get pleroma tan back
1837 |> assign(:user, user)
1838 |> get("/api/v1/pleroma/mascot")
1840 assert %{"url" => url} = json_response(conn, 200)
1841 assert url =~ "pleroma-fox-tan-smol"
1843 # When a user sets their mascot, we should get that back
1844 file = %Plug.Upload{
1845 content_type: "image/jpg",
1846 path: Path.absname("test/fixtures/image.jpg"),
1847 filename: "an_image.jpg"
1852 |> assign(:user, user)
1853 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1855 assert json_response(conn, 200)
1857 user = User.get_cached_by_id(user.id)
1861 |> assign(:user, user)
1862 |> get("/api/v1/pleroma/mascot")
1864 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1865 assert url =~ "an_image"
1868 test "hashtag timeline", %{conn: conn} do
1869 following = insert(:user)
1872 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1874 {:ok, [_activity]} =
1875 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1879 |> get("/api/v1/timelines/tag/2hu")
1881 assert [%{"id" => id}] = json_response(nconn, 200)
1883 assert id == to_string(activity.id)
1885 # works for different capitalization too
1888 |> get("/api/v1/timelines/tag/2HU")
1890 assert [%{"id" => id}] = json_response(nconn, 200)
1892 assert id == to_string(activity.id)
1896 test "multi-hashtag timeline", %{conn: conn} do
1897 user = insert(:user)
1899 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1900 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1901 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1905 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1907 [status_none, status_test1, status_test] = json_response(any_test, 200)
1909 assert to_string(activity_test.id) == status_test["id"]
1910 assert to_string(activity_test1.id) == status_test1["id"]
1911 assert to_string(activity_none.id) == status_none["id"]
1915 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1917 assert [status_test1] == json_response(restricted_test, 200)
1919 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1921 assert [status_none] == json_response(all_test, 200)
1924 test "getting followers", %{conn: conn} do
1925 user = insert(:user)
1926 other_user = insert(:user)
1927 {:ok, user} = User.follow(user, other_user)
1931 |> get("/api/v1/accounts/#{other_user.id}/followers")
1933 assert [%{"id" => id}] = json_response(conn, 200)
1934 assert id == to_string(user.id)
1937 test "getting followers, hide_followers", %{conn: conn} do
1938 user = insert(:user)
1939 other_user = insert(:user, %{info: %{hide_followers: true}})
1940 {:ok, _user} = User.follow(user, other_user)
1944 |> get("/api/v1/accounts/#{other_user.id}/followers")
1946 assert [] == json_response(conn, 200)
1949 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1950 user = insert(:user)
1951 other_user = insert(:user, %{info: %{hide_followers: true}})
1952 {:ok, _user} = User.follow(user, other_user)
1956 |> assign(:user, other_user)
1957 |> get("/api/v1/accounts/#{other_user.id}/followers")
1959 refute [] == json_response(conn, 200)
1962 test "getting followers, pagination", %{conn: conn} do
1963 user = insert(:user)
1964 follower1 = insert(:user)
1965 follower2 = insert(:user)
1966 follower3 = insert(:user)
1967 {:ok, _} = User.follow(follower1, user)
1968 {:ok, _} = User.follow(follower2, user)
1969 {:ok, _} = User.follow(follower3, user)
1973 |> assign(:user, user)
1977 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1979 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1980 assert id3 == follower3.id
1981 assert id2 == follower2.id
1985 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1987 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1988 assert id2 == follower2.id
1989 assert id1 == follower1.id
1993 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1995 assert [%{"id" => id2}] = json_response(res_conn, 200)
1996 assert id2 == follower2.id
1998 assert [link_header] = get_resp_header(res_conn, "link")
1999 assert link_header =~ ~r/min_id=#{follower2.id}/
2000 assert link_header =~ ~r/max_id=#{follower2.id}/
2003 test "getting following", %{conn: conn} do
2004 user = insert(:user)
2005 other_user = insert(:user)
2006 {:ok, user} = User.follow(user, other_user)
2010 |> get("/api/v1/accounts/#{user.id}/following")
2012 assert [%{"id" => id}] = json_response(conn, 200)
2013 assert id == to_string(other_user.id)
2016 test "getting following, hide_follows", %{conn: conn} do
2017 user = insert(:user, %{info: %{hide_follows: true}})
2018 other_user = insert(:user)
2019 {:ok, user} = User.follow(user, other_user)
2023 |> get("/api/v1/accounts/#{user.id}/following")
2025 assert [] == json_response(conn, 200)
2028 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2029 user = insert(:user, %{info: %{hide_follows: true}})
2030 other_user = insert(:user)
2031 {:ok, user} = User.follow(user, other_user)
2035 |> assign(:user, user)
2036 |> get("/api/v1/accounts/#{user.id}/following")
2038 refute [] == json_response(conn, 200)
2041 test "getting following, pagination", %{conn: conn} do
2042 user = insert(:user)
2043 following1 = insert(:user)
2044 following2 = insert(:user)
2045 following3 = insert(:user)
2046 {:ok, _} = User.follow(user, following1)
2047 {:ok, _} = User.follow(user, following2)
2048 {:ok, _} = User.follow(user, following3)
2052 |> assign(:user, user)
2056 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2058 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2059 assert id3 == following3.id
2060 assert id2 == following2.id
2064 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2066 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2067 assert id2 == following2.id
2068 assert id1 == following1.id
2072 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2074 assert [%{"id" => id2}] = json_response(res_conn, 200)
2075 assert id2 == following2.id
2077 assert [link_header] = get_resp_header(res_conn, "link")
2078 assert link_header =~ ~r/min_id=#{following2.id}/
2079 assert link_header =~ ~r/max_id=#{following2.id}/
2082 test "following / unfollowing a user", %{conn: conn} do
2083 user = insert(:user)
2084 other_user = insert(:user)
2088 |> assign(:user, user)
2089 |> post("/api/v1/accounts/#{other_user.id}/follow")
2091 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2093 user = User.get_cached_by_id(user.id)
2097 |> assign(:user, user)
2098 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2100 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2102 user = User.get_cached_by_id(user.id)
2106 |> assign(:user, user)
2107 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2109 assert %{"id" => id} = json_response(conn, 200)
2110 assert id == to_string(other_user.id)
2113 test "following without reblogs" do
2114 follower = insert(:user)
2115 followed = insert(:user)
2116 other_user = insert(:user)
2120 |> assign(:user, follower)
2121 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2123 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2125 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2126 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2130 |> assign(:user, User.get_cached_by_id(follower.id))
2131 |> get("/api/v1/timelines/home")
2133 assert [] == json_response(conn, 200)
2137 |> assign(:user, follower)
2138 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2140 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2144 |> assign(:user, User.get_cached_by_id(follower.id))
2145 |> get("/api/v1/timelines/home")
2147 expected_activity_id = reblog.id
2148 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2151 test "following / unfollowing errors" do
2152 user = insert(:user)
2156 |> assign(:user, user)
2159 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2160 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2163 user = User.get_cached_by_id(user.id)
2164 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2165 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2167 # self follow via uri
2168 user = User.get_cached_by_id(user.id)
2169 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2170 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2172 # follow non existing user
2173 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2174 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2176 # follow non existing user via uri
2177 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2178 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2180 # unfollow non existing user
2181 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2182 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2185 describe "mute/unmute" do
2186 test "with notifications", %{conn: conn} do
2187 user = insert(:user)
2188 other_user = insert(:user)
2192 |> assign(:user, user)
2193 |> post("/api/v1/accounts/#{other_user.id}/mute")
2195 response = json_response(conn, 200)
2197 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2198 user = User.get_cached_by_id(user.id)
2202 |> assign(:user, user)
2203 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2205 response = json_response(conn, 200)
2206 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2209 test "without notifications", %{conn: conn} do
2210 user = insert(:user)
2211 other_user = insert(:user)
2215 |> assign(:user, user)
2216 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2218 response = json_response(conn, 200)
2220 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2221 user = User.get_cached_by_id(user.id)
2225 |> assign(:user, user)
2226 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2228 response = json_response(conn, 200)
2229 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2233 test "subscribing / unsubscribing to a user", %{conn: conn} do
2234 user = insert(:user)
2235 subscription_target = insert(:user)
2239 |> assign(:user, user)
2240 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2242 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2246 |> assign(:user, user)
2247 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2249 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2252 test "getting a list of mutes", %{conn: conn} do
2253 user = insert(:user)
2254 other_user = insert(:user)
2256 {:ok, user} = User.mute(user, other_user)
2260 |> assign(:user, user)
2261 |> get("/api/v1/mutes")
2263 other_user_id = to_string(other_user.id)
2264 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2267 test "blocking / unblocking a user", %{conn: conn} do
2268 user = insert(:user)
2269 other_user = insert(:user)
2273 |> assign(:user, user)
2274 |> post("/api/v1/accounts/#{other_user.id}/block")
2276 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2278 user = User.get_cached_by_id(user.id)
2282 |> assign(:user, user)
2283 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2285 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2288 test "getting a list of blocks", %{conn: conn} do
2289 user = insert(:user)
2290 other_user = insert(:user)
2292 {:ok, user} = User.block(user, other_user)
2296 |> assign(:user, user)
2297 |> get("/api/v1/blocks")
2299 other_user_id = to_string(other_user.id)
2300 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2303 test "blocking / unblocking a domain", %{conn: conn} do
2304 user = insert(:user)
2305 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2309 |> assign(:user, user)
2310 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2312 assert %{} = json_response(conn, 200)
2313 user = User.get_cached_by_ap_id(user.ap_id)
2314 assert User.blocks?(user, other_user)
2318 |> assign(:user, user)
2319 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2321 assert %{} = json_response(conn, 200)
2322 user = User.get_cached_by_ap_id(user.ap_id)
2323 refute User.blocks?(user, other_user)
2326 test "getting a list of domain blocks", %{conn: conn} do
2327 user = insert(:user)
2329 {:ok, user} = User.block_domain(user, "bad.site")
2330 {:ok, user} = User.block_domain(user, "even.worse.site")
2334 |> assign(:user, user)
2335 |> get("/api/v1/domain_blocks")
2337 domain_blocks = json_response(conn, 200)
2339 assert "bad.site" in domain_blocks
2340 assert "even.worse.site" in domain_blocks
2343 test "unimplemented follow_requests, blocks, domain blocks" do
2344 user = insert(:user)
2346 ["blocks", "domain_blocks", "follow_requests"]
2347 |> Enum.each(fn endpoint ->
2350 |> assign(:user, user)
2351 |> get("/api/v1/#{endpoint}")
2353 assert [] = json_response(conn, 200)
2357 test "returns the favorites of a user", %{conn: conn} do
2358 user = insert(:user)
2359 other_user = insert(:user)
2361 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2362 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2364 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2368 |> assign(:user, user)
2369 |> get("/api/v1/favourites")
2371 assert [status] = json_response(first_conn, 200)
2372 assert status["id"] == to_string(activity.id)
2374 assert [{"link", _link_header}] =
2375 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2377 # Honours query params
2378 {:ok, second_activity} =
2379 CommonAPI.post(other_user, %{
2381 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2384 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2386 last_like = status["id"]
2390 |> assign(:user, user)
2391 |> get("/api/v1/favourites?since_id=#{last_like}")
2393 assert [second_status] = json_response(second_conn, 200)
2394 assert second_status["id"] == to_string(second_activity.id)
2398 |> assign(:user, user)
2399 |> get("/api/v1/favourites?limit=0")
2401 assert [] = json_response(third_conn, 200)
2404 describe "getting favorites timeline of specified user" do
2406 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2407 [current_user: current_user, user: user]
2410 test "returns list of statuses favorited by specified user", %{
2412 current_user: current_user,
2415 [activity | _] = insert_pair(:note_activity)
2416 CommonAPI.favorite(activity.id, user)
2420 |> assign(:user, current_user)
2421 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2422 |> json_response(:ok)
2426 assert length(response) == 1
2427 assert like["id"] == activity.id
2430 test "returns favorites for specified user_id when user is not logged in", %{
2434 activity = insert(:note_activity)
2435 CommonAPI.favorite(activity.id, user)
2439 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2440 |> json_response(:ok)
2442 assert length(response) == 1
2445 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2447 current_user: current_user,
2451 CommonAPI.post(current_user, %{
2452 "status" => "Hi @#{user.nickname}!",
2453 "visibility" => "direct"
2456 CommonAPI.favorite(direct.id, user)
2460 |> assign(:user, current_user)
2461 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2462 |> json_response(:ok)
2464 assert length(response) == 1
2466 anonymous_response =
2468 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2469 |> json_response(:ok)
2471 assert Enum.empty?(anonymous_response)
2474 test "does not return others' favorited DM when user is not one of recipients", %{
2476 current_user: current_user,
2479 user_two = insert(:user)
2482 CommonAPI.post(user_two, %{
2483 "status" => "Hi @#{user.nickname}!",
2484 "visibility" => "direct"
2487 CommonAPI.favorite(direct.id, user)
2491 |> assign(:user, current_user)
2492 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2493 |> json_response(:ok)
2495 assert Enum.empty?(response)
2498 test "paginates favorites using since_id and max_id", %{
2500 current_user: current_user,
2503 activities = insert_list(10, :note_activity)
2505 Enum.each(activities, fn activity ->
2506 CommonAPI.favorite(activity.id, user)
2509 third_activity = Enum.at(activities, 2)
2510 seventh_activity = Enum.at(activities, 6)
2514 |> assign(:user, current_user)
2515 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2516 since_id: third_activity.id,
2517 max_id: seventh_activity.id
2519 |> json_response(:ok)
2521 assert length(response) == 3
2522 refute third_activity in response
2523 refute seventh_activity in response
2526 test "limits favorites using limit parameter", %{
2528 current_user: current_user,
2532 |> insert_list(:note_activity)
2533 |> Enum.each(fn activity ->
2534 CommonAPI.favorite(activity.id, user)
2539 |> assign(:user, current_user)
2540 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2541 |> json_response(:ok)
2543 assert length(response) == 3
2546 test "returns empty response when user does not have any favorited statuses", %{
2548 current_user: current_user,
2553 |> assign(:user, current_user)
2554 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2555 |> json_response(:ok)
2557 assert Enum.empty?(response)
2560 test "returns 404 error when specified user is not exist", %{conn: conn} do
2561 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2563 assert json_response(conn, 404) == %{"error" => "Record not found"}
2566 test "returns 403 error when user has hidden own favorites", %{
2568 current_user: current_user
2570 user = insert(:user, %{info: %{hide_favorites: true}})
2571 activity = insert(:note_activity)
2572 CommonAPI.favorite(activity.id, user)
2576 |> assign(:user, current_user)
2577 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2579 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2582 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2583 user = insert(:user)
2584 activity = insert(:note_activity)
2585 CommonAPI.favorite(activity.id, user)
2589 |> assign(:user, current_user)
2590 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2592 assert user.info.hide_favorites
2593 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2597 test "get instance information", %{conn: conn} do
2598 conn = get(conn, "/api/v1/instance")
2599 assert result = json_response(conn, 200)
2601 email = Config.get([:instance, :email])
2602 # Note: not checking for "max_toot_chars" since it's optional
2608 "email" => from_config_email,
2610 "streaming_api" => _
2615 "registrations" => _,
2619 assert email == from_config_email
2622 test "get instance stats", %{conn: conn} do
2623 user = insert(:user, %{local: true})
2625 user2 = insert(:user, %{local: true})
2626 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2628 insert(:user, %{local: false, nickname: "u@peer1.com"})
2629 insert(:user, %{local: false, nickname: "u@peer2.com"})
2631 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2633 # Stats should count users with missing or nil `info.deactivated` value
2634 user = User.get_cached_by_id(user.id)
2635 info_change = Changeset.change(user.info, %{deactivated: nil})
2639 |> Changeset.change()
2640 |> Changeset.put_embed(:info, info_change)
2641 |> User.update_and_set_cache()
2643 Pleroma.Stats.force_update()
2645 conn = get(conn, "/api/v1/instance")
2647 assert result = json_response(conn, 200)
2649 stats = result["stats"]
2652 assert stats["user_count"] == 1
2653 assert stats["status_count"] == 1
2654 assert stats["domain_count"] == 2
2657 test "get peers", %{conn: conn} do
2658 insert(:user, %{local: false, nickname: "u@peer1.com"})
2659 insert(:user, %{local: false, nickname: "u@peer2.com"})
2661 Pleroma.Stats.force_update()
2663 conn = get(conn, "/api/v1/instance/peers")
2665 assert result = json_response(conn, 200)
2667 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2670 test "put settings", %{conn: conn} do
2671 user = insert(:user)
2675 |> assign(:user, user)
2676 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2678 assert _result = json_response(conn, 200)
2680 user = User.get_cached_by_ap_id(user.ap_id)
2681 assert user.info.settings == %{"programming" => "socks"}
2684 describe "pinned statuses" do
2686 user = insert(:user)
2687 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2689 [user: user, activity: activity]
2692 clear_config([:instance, :max_pinned_statuses]) do
2693 Config.put([:instance, :max_pinned_statuses], 1)
2696 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2697 {:ok, _} = CommonAPI.pin(activity.id, user)
2701 |> assign(:user, user)
2702 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2703 |> json_response(200)
2705 id_str = to_string(activity.id)
2707 assert [%{"id" => ^id_str, "pinned" => true}] = result
2710 test "pin status", %{conn: conn, user: user, activity: activity} do
2711 id_str = to_string(activity.id)
2713 assert %{"id" => ^id_str, "pinned" => true} =
2715 |> assign(:user, user)
2716 |> post("/api/v1/statuses/#{activity.id}/pin")
2717 |> json_response(200)
2719 assert [%{"id" => ^id_str, "pinned" => true}] =
2721 |> assign(:user, user)
2722 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2723 |> json_response(200)
2726 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2727 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2731 |> assign(:user, user)
2732 |> post("/api/v1/statuses/#{dm.id}/pin")
2734 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2737 test "unpin status", %{conn: conn, user: user, activity: activity} do
2738 {:ok, _} = CommonAPI.pin(activity.id, user)
2740 id_str = to_string(activity.id)
2741 user = refresh_record(user)
2743 assert %{"id" => ^id_str, "pinned" => false} =
2745 |> assign(:user, user)
2746 |> post("/api/v1/statuses/#{activity.id}/unpin")
2747 |> json_response(200)
2751 |> assign(:user, user)
2752 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2753 |> json_response(200)
2756 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2759 |> assign(:user, user)
2760 |> post("/api/v1/statuses/1/unpin")
2762 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2765 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2766 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2768 id_str_one = to_string(activity_one.id)
2770 assert %{"id" => ^id_str_one, "pinned" => true} =
2772 |> assign(:user, user)
2773 |> post("/api/v1/statuses/#{id_str_one}/pin")
2774 |> json_response(200)
2776 user = refresh_record(user)
2778 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2780 |> assign(:user, user)
2781 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2782 |> json_response(400)
2788 Config.put([:rich_media, :enabled], true)
2790 user = insert(:user)
2794 test "returns rich-media card", %{conn: conn, user: user} do
2795 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2798 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2799 "provider_name" => "example.com",
2800 "provider_url" => "https://example.com",
2801 "title" => "The Rock",
2803 "url" => "https://example.com/ogp",
2805 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2808 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2809 "title" => "The Rock",
2810 "type" => "video.movie",
2811 "url" => "https://example.com/ogp",
2813 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2820 |> get("/api/v1/statuses/#{activity.id}/card")
2821 |> json_response(200)
2823 assert response == card_data
2825 # works with private posts
2827 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2831 |> assign(:user, user)
2832 |> get("/api/v1/statuses/#{activity.id}/card")
2833 |> json_response(200)
2835 assert response_two == card_data
2838 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2840 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2844 |> get("/api/v1/statuses/#{activity.id}/card")
2845 |> json_response(:ok)
2847 assert response == %{
2849 "title" => "Pleroma",
2850 "description" => "",
2852 "provider_name" => "example.com",
2853 "provider_url" => "https://example.com",
2854 "url" => "https://example.com/ogp-missing-data",
2857 "title" => "Pleroma",
2858 "type" => "website",
2859 "url" => "https://example.com/ogp-missing-data"
2867 user = insert(:user)
2868 for_user = insert(:user)
2871 CommonAPI.post(user, %{
2872 "status" => "heweoo?"
2876 CommonAPI.post(user, %{
2877 "status" => "heweoo!"
2882 |> assign(:user, for_user)
2883 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2885 assert json_response(response1, 200)["bookmarked"] == true
2889 |> assign(:user, for_user)
2890 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2892 assert json_response(response2, 200)["bookmarked"] == true
2896 |> assign(:user, for_user)
2897 |> get("/api/v1/bookmarks")
2899 assert [json_response(response2, 200), json_response(response1, 200)] ==
2900 json_response(bookmarks, 200)
2904 |> assign(:user, for_user)
2905 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2907 assert json_response(response1, 200)["bookmarked"] == false
2911 |> assign(:user, for_user)
2912 |> get("/api/v1/bookmarks")
2914 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2917 describe "conversation muting" do
2919 post_user = insert(:user)
2920 user = insert(:user)
2922 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2924 [user: user, activity: activity]
2927 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2928 id_str = to_string(activity.id)
2930 assert %{"id" => ^id_str, "muted" => true} =
2932 |> assign(:user, user)
2933 |> post("/api/v1/statuses/#{activity.id}/mute")
2934 |> json_response(200)
2937 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2938 {:ok, _} = CommonAPI.add_mute(user, activity)
2942 |> assign(:user, user)
2943 |> post("/api/v1/statuses/#{activity.id}/mute")
2945 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2948 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2949 {:ok, _} = CommonAPI.add_mute(user, activity)
2951 id_str = to_string(activity.id)
2952 user = refresh_record(user)
2954 assert %{"id" => ^id_str, "muted" => false} =
2956 |> assign(:user, user)
2957 |> post("/api/v1/statuses/#{activity.id}/unmute")
2958 |> json_response(200)
2962 describe "reports" do
2964 reporter = insert(:user)
2965 target_user = insert(:user)
2967 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2969 [reporter: reporter, target_user: target_user, activity: activity]
2972 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2973 assert %{"action_taken" => false, "id" => _} =
2975 |> assign(:user, reporter)
2976 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2977 |> json_response(200)
2980 test "submit a report with statuses and comment", %{
2983 target_user: target_user,
2986 assert %{"action_taken" => false, "id" => _} =
2988 |> assign(:user, reporter)
2989 |> post("/api/v1/reports", %{
2990 "account_id" => target_user.id,
2991 "status_ids" => [activity.id],
2992 "comment" => "bad status!",
2993 "forward" => "false"
2995 |> json_response(200)
2998 test "account_id is required", %{
3003 assert %{"error" => "Valid `account_id` required"} =
3005 |> assign(:user, reporter)
3006 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3007 |> json_response(400)
3010 test "comment must be up to the size specified in the config", %{
3013 target_user: target_user
3015 max_size = Config.get([:instance, :max_report_comment_size], 1000)
3016 comment = String.pad_trailing("a", max_size + 1, "a")
3018 error = %{"error" => "Comment must be up to #{max_size} characters"}
3022 |> assign(:user, reporter)
3023 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3024 |> json_response(400)
3027 test "returns error when account is not exist", %{
3034 |> assign(:user, reporter)
3035 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3037 assert json_response(conn, 400) == %{"error" => "Account not found"}
3041 describe "link headers" do
3042 test "preserves parameters in link headers", %{conn: conn} do
3043 user = insert(:user)
3044 other_user = insert(:user)
3047 CommonAPI.post(other_user, %{
3048 "status" => "hi @#{user.nickname}",
3049 "visibility" => "public"
3053 CommonAPI.post(other_user, %{
3054 "status" => "hi @#{user.nickname}",
3055 "visibility" => "public"
3058 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3059 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3063 |> assign(:user, user)
3064 |> get("/api/v1/notifications", %{media_only: true})
3066 assert [link_header] = get_resp_header(conn, "link")
3067 assert link_header =~ ~r/media_only=true/
3068 assert link_header =~ ~r/min_id=#{notification2.id}/
3069 assert link_header =~ ~r/max_id=#{notification1.id}/
3073 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3074 # Need to set an old-style integer ID to reproduce the problem
3075 # (these are no longer assigned to new accounts but were preserved
3076 # for existing accounts during the migration to flakeIDs)
3077 user_one = insert(:user, %{id: 1212})
3078 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3082 |> get("/api/v1/accounts/#{user_one.id}")
3086 |> get("/api/v1/accounts/#{user_two.nickname}")
3090 |> get("/api/v1/accounts/#{user_two.id}")
3092 acc_one = json_response(resp_one, 200)
3093 acc_two = json_response(resp_two, 200)
3094 acc_three = json_response(resp_three, 200)
3095 refute acc_one == acc_two
3096 assert acc_two == acc_three
3099 describe "custom emoji" do
3100 test "with tags", %{conn: conn} do
3103 |> get("/api/v1/custom_emojis")
3104 |> json_response(200)
3106 assert Map.has_key?(emoji, "shortcode")
3107 assert Map.has_key?(emoji, "static_url")
3108 assert Map.has_key?(emoji, "tags")
3109 assert is_list(emoji["tags"])
3110 assert Map.has_key?(emoji, "category")
3111 assert Map.has_key?(emoji, "url")
3112 assert Map.has_key?(emoji, "visible_in_picker")
3116 describe "index/2 redirections" do
3117 setup %{conn: conn} do
3121 signing_salt: "cooldude"
3126 |> Plug.Session.call(Plug.Session.init(session_opts))
3129 test_path = "/web/statuses/test"
3130 %{conn: conn, path: test_path}
3133 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3134 conn = get(conn, path)
3136 assert conn.status == 302
3137 assert redirected_to(conn) == "/web/login"
3140 test "redirects not logged-in users to the login page on private instances", %{
3144 Config.put([:instance, :public], false)
3146 conn = get(conn, path)
3148 assert conn.status == 302
3149 assert redirected_to(conn) == "/web/login"
3152 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3153 token = insert(:oauth_token)
3157 |> assign(:user, token.user)
3158 |> put_session(:oauth_token, token.token)
3161 assert conn.status == 200
3164 test "saves referer path to session", %{conn: conn, path: path} do
3165 conn = get(conn, path)
3166 return_to = Plug.Conn.get_session(conn, :return_to)
3168 assert return_to == path
3171 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3172 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3173 auth = insert(:oauth_authorization, app: app)
3177 |> put_session(:return_to, path)
3178 |> get("/web/login", %{code: auth.token})
3180 assert conn.status == 302
3181 assert redirected_to(conn) == path
3184 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3185 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3186 auth = insert(:oauth_authorization, app: app)
3188 conn = get(conn, "/web/login", %{code: auth.token})
3190 assert conn.status == 302
3191 assert redirected_to(conn) == "/web/getting-started"
3195 describe "scheduled activities" do
3196 test "creates a scheduled activity", %{conn: conn} do
3197 user = insert(:user)
3198 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3202 |> assign(:user, user)
3203 |> post("/api/v1/statuses", %{
3204 "status" => "scheduled",
3205 "scheduled_at" => scheduled_at
3208 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3209 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3210 assert [] == Repo.all(Activity)
3213 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3214 user = insert(:user)
3215 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3217 file = %Plug.Upload{
3218 content_type: "image/jpg",
3219 path: Path.absname("test/fixtures/image.jpg"),
3220 filename: "an_image.jpg"
3223 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3227 |> assign(:user, user)
3228 |> post("/api/v1/statuses", %{
3229 "media_ids" => [to_string(upload.id)],
3230 "status" => "scheduled",
3231 "scheduled_at" => scheduled_at
3234 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3235 assert %{"type" => "image"} = media_attachment
3238 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3240 user = insert(:user)
3243 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3247 |> assign(:user, user)
3248 |> post("/api/v1/statuses", %{
3249 "status" => "not scheduled",
3250 "scheduled_at" => scheduled_at
3253 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3254 assert [] == Repo.all(ScheduledActivity)
3257 test "returns error when daily user limit is exceeded", %{conn: conn} do
3258 user = insert(:user)
3261 NaiveDateTime.utc_now()
3262 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3263 |> NaiveDateTime.to_iso8601()
3265 attrs = %{params: %{}, scheduled_at: today}
3266 {:ok, _} = ScheduledActivity.create(user, attrs)
3267 {:ok, _} = ScheduledActivity.create(user, attrs)
3271 |> assign(:user, user)
3272 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3274 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3277 test "returns error when total user limit is exceeded", %{conn: conn} do
3278 user = insert(:user)
3281 NaiveDateTime.utc_now()
3282 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3283 |> NaiveDateTime.to_iso8601()
3286 NaiveDateTime.utc_now()
3287 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3288 |> NaiveDateTime.to_iso8601()
3290 attrs = %{params: %{}, scheduled_at: today}
3291 {:ok, _} = ScheduledActivity.create(user, attrs)
3292 {:ok, _} = ScheduledActivity.create(user, attrs)
3293 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3297 |> assign(:user, user)
3298 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3300 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3303 test "shows scheduled activities", %{conn: conn} do
3304 user = insert(:user)
3305 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3306 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3307 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3308 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3312 |> assign(:user, user)
3317 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3319 result = json_response(conn_res, 200)
3320 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3325 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3327 result = json_response(conn_res, 200)
3328 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3333 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3335 result = json_response(conn_res, 200)
3336 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3339 test "shows a scheduled activity", %{conn: conn} do
3340 user = insert(:user)
3341 scheduled_activity = insert(:scheduled_activity, user: user)
3345 |> assign(:user, user)
3346 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3348 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3349 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3353 |> assign(:user, user)
3354 |> get("/api/v1/scheduled_statuses/404")
3356 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3359 test "updates a scheduled activity", %{conn: conn} do
3360 user = insert(:user)
3361 scheduled_activity = insert(:scheduled_activity, user: user)
3364 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3368 |> assign(:user, user)
3369 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3370 scheduled_at: new_scheduled_at
3373 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3374 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3378 |> assign(:user, user)
3379 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3381 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3384 test "deletes a scheduled activity", %{conn: conn} do
3385 user = insert(:user)
3386 scheduled_activity = insert(:scheduled_activity, user: user)
3390 |> assign(:user, user)
3391 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3393 assert %{} = json_response(res_conn, 200)
3394 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3398 |> assign(:user, user)
3399 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3401 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3405 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3406 user1 = insert(:user)
3407 user2 = insert(:user)
3408 user3 = insert(:user)
3410 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3412 # Reply to status from another user
3415 |> assign(:user, user2)
3416 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3418 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3420 activity = Activity.get_by_id_with_object(id)
3422 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3423 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3425 # Reblog from the third user
3428 |> assign(:user, user3)
3429 |> post("/api/v1/statuses/#{activity.id}/reblog")
3431 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3432 json_response(conn2, 200)
3434 assert to_string(activity.id) == id
3436 # Getting third user status
3439 |> assign(:user, user3)
3440 |> get("api/v1/timelines/home")
3442 [reblogged_activity] = json_response(conn3, 200)
3444 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3446 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3447 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3450 describe "create account by app" do
3451 test "Account registration via Application", %{conn: conn} do
3454 |> post("/api/v1/apps", %{
3455 client_name: "client_name",
3456 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3457 scopes: "read, write, follow"
3461 "client_id" => client_id,
3462 "client_secret" => client_secret,
3464 "name" => "client_name",
3465 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3468 } = json_response(conn, 200)
3472 |> post("/oauth/token", %{
3473 grant_type: "client_credentials",
3474 client_id: client_id,
3475 client_secret: client_secret
3478 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3479 json_response(conn, 200)
3482 token_from_db = Repo.get_by(Token, token: token)
3483 assert token_from_db
3485 assert scope == "read write follow"
3489 |> put_req_header("authorization", "Bearer " <> token)
3490 |> post("/api/v1/accounts", %{
3492 email: "lain@example.org",
3493 password: "PlzDontHackLain",
3498 "access_token" => token,
3499 "created_at" => _created_at,
3501 "token_type" => "Bearer"
3502 } = json_response(conn, 200)
3504 token_from_db = Repo.get_by(Token, token: token)
3505 assert token_from_db
3506 token_from_db = Repo.preload(token_from_db, :user)
3507 assert token_from_db.user
3509 assert token_from_db.user.info.confirmation_pending
3512 test "rate limit", %{conn: conn} do
3513 app_token = insert(:oauth_token, user: nil)
3516 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3517 |> Map.put(:remote_ip, {15, 15, 15, 15})
3522 |> post("/api/v1/accounts", %{
3523 username: "#{i}lain",
3524 email: "#{i}lain@example.org",
3525 password: "PlzDontHackLain",
3530 "access_token" => token,
3531 "created_at" => _created_at,
3533 "token_type" => "Bearer"
3534 } = json_response(conn, 200)
3536 token_from_db = Repo.get_by(Token, token: token)
3537 assert token_from_db
3538 token_from_db = Repo.preload(token_from_db, :user)
3539 assert token_from_db.user
3541 assert token_from_db.user.info.confirmation_pending
3546 |> post("/api/v1/accounts", %{
3548 email: "6lain@example.org",
3549 password: "PlzDontHackLain",
3553 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3557 describe "GET /api/v1/polls/:id" do
3558 test "returns poll entity for object id", %{conn: conn} do
3559 user = insert(:user)
3562 CommonAPI.post(user, %{
3563 "status" => "Pleroma does",
3564 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3567 object = Object.normalize(activity)
3571 |> assign(:user, user)
3572 |> get("/api/v1/polls/#{object.id}")
3574 response = json_response(conn, 200)
3575 id = to_string(object.id)
3576 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3579 test "does not expose polls for private statuses", %{conn: conn} do
3580 user = insert(:user)
3581 other_user = insert(:user)
3584 CommonAPI.post(user, %{
3585 "status" => "Pleroma does",
3586 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3587 "visibility" => "private"
3590 object = Object.normalize(activity)
3594 |> assign(:user, other_user)
3595 |> get("/api/v1/polls/#{object.id}")
3597 assert json_response(conn, 404)
3601 describe "POST /api/v1/polls/:id/votes" do
3602 test "votes are added to the poll", %{conn: conn} do
3603 user = insert(:user)
3604 other_user = insert(:user)
3607 CommonAPI.post(user, %{
3608 "status" => "A very delicious sandwich",
3610 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3616 object = Object.normalize(activity)
3620 |> assign(:user, other_user)
3621 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3623 assert json_response(conn, 200)
3624 object = Object.get_by_id(object.id)
3626 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3631 test "author can't vote", %{conn: conn} do
3632 user = insert(:user)
3635 CommonAPI.post(user, %{
3636 "status" => "Am I cute?",
3637 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3640 object = Object.normalize(activity)
3643 |> assign(:user, user)
3644 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3645 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3647 object = Object.get_by_id(object.id)
3649 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3652 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3653 user = insert(:user)
3654 other_user = insert(:user)
3657 CommonAPI.post(user, %{
3658 "status" => "The glass is",
3659 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3662 object = Object.normalize(activity)
3665 |> assign(:user, other_user)
3666 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3667 |> json_response(422) == %{"error" => "Too many choices"}
3669 object = Object.get_by_id(object.id)
3671 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3676 test "does not allow choice index to be greater than options count", %{conn: conn} do
3677 user = insert(:user)
3678 other_user = insert(:user)
3681 CommonAPI.post(user, %{
3682 "status" => "Am I cute?",
3683 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3686 object = Object.normalize(activity)
3690 |> assign(:user, other_user)
3691 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3693 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3696 test "returns 404 error when object is not exist", %{conn: conn} do
3697 user = insert(:user)
3701 |> assign(:user, user)
3702 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3704 assert json_response(conn, 404) == %{"error" => "Record not found"}
3707 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3708 user = insert(:user)
3709 other_user = insert(:user)
3712 CommonAPI.post(user, %{
3713 "status" => "Am I cute?",
3714 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3715 "visibility" => "private"
3718 object = Object.normalize(activity)
3722 |> assign(:user, other_user)
3723 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3725 assert json_response(conn, 404) == %{"error" => "Record not found"}
3729 describe "GET /api/v1/statuses/:id/favourited_by" do
3731 user = insert(:user)
3732 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3736 |> assign(:user, user)
3738 [conn: conn, activity: activity]
3741 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3742 other_user = insert(:user)
3743 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3747 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3748 |> json_response(:ok)
3750 [%{"id" => id}] = response
3752 assert id == other_user.id
3755 test "returns empty array when status has not been favorited yet", %{
3761 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3762 |> json_response(:ok)
3764 assert Enum.empty?(response)
3767 test "does not return users who have favorited the status but are blocked", %{
3768 conn: %{assigns: %{user: user}} = conn,
3771 other_user = insert(:user)
3772 {:ok, user} = User.block(user, other_user)
3774 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3778 |> assign(:user, user)
3779 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3780 |> json_response(:ok)
3782 assert Enum.empty?(response)
3785 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3786 other_user = insert(:user)
3787 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3791 |> assign(:user, nil)
3792 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3793 |> json_response(:ok)
3795 [%{"id" => id}] = response
3796 assert id == other_user.id
3800 describe "GET /api/v1/statuses/:id/reblogged_by" do
3802 user = insert(:user)
3803 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3807 |> assign(:user, user)
3809 [conn: conn, activity: activity]
3812 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3813 other_user = insert(:user)
3814 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3818 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3819 |> json_response(:ok)
3821 [%{"id" => id}] = response
3823 assert id == other_user.id
3826 test "returns empty array when status has not been reblogged yet", %{
3832 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3833 |> json_response(:ok)
3835 assert Enum.empty?(response)
3838 test "does not return users who have reblogged the status but are blocked", %{
3839 conn: %{assigns: %{user: user}} = conn,
3842 other_user = insert(:user)
3843 {:ok, user} = User.block(user, other_user)
3845 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3849 |> assign(:user, user)
3850 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3851 |> json_response(:ok)
3853 assert Enum.empty?(response)
3856 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3857 other_user = insert(:user)
3858 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3862 |> assign(:user, nil)
3863 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3864 |> json_response(:ok)
3866 [%{"id" => id}] = response
3867 assert id == other_user.id
3871 describe "POST /auth/password, with valid parameters" do
3872 setup %{conn: conn} do
3873 user = insert(:user)
3874 conn = post(conn, "/auth/password?email=#{user.email}")
3875 %{conn: conn, user: user}
3878 test "it returns 204", %{conn: conn} do
3879 assert json_response(conn, :no_content)
3882 test "it creates a PasswordResetToken record for user", %{user: user} do
3883 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3887 test "it sends an email to user", %{user: user} do
3888 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3890 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3891 notify_email = Config.get([:instance, :notify_email])
3892 instance_name = Config.get([:instance, :name])
3895 from: {instance_name, notify_email},
3896 to: {user.name, user.email},
3897 html_body: email.html_body
3902 describe "POST /auth/password, with invalid parameters" do
3904 user = insert(:user)
3908 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3909 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3910 assert conn.status == 404
3911 assert conn.resp_body == ""
3914 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3915 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3916 conn = post(conn, "/auth/password?email=#{user.email}")
3917 assert conn.status == 400
3918 assert conn.resp_body == ""
3922 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3924 user = insert(:user)
3925 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3929 |> Changeset.change()
3930 |> Changeset.put_embed(:info, info_change)
3933 assert user.info.confirmation_pending
3938 clear_config([:instance, :account_activation_required]) do
3939 Config.put([:instance, :account_activation_required], true)
3942 test "resend account confirmation email", %{conn: conn, user: user} do
3944 |> assign(:user, user)
3945 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3946 |> json_response(:no_content)
3948 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3949 notify_email = Config.get([:instance, :notify_email])
3950 instance_name = Config.get([:instance, :name])
3953 from: {instance_name, notify_email},
3954 to: {user.name, user.email},
3955 html_body: email.html_body
3960 describe "GET /api/v1/suggestions" do
3962 user = insert(:user)
3963 other_user = insert(:user)
3964 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3965 url500 = "http://test500?#{host}&#{user.nickname}"
3966 url200 = "http://test200?#{host}&#{user.nickname}"
3969 %{method: :get, url: ^url500} ->
3970 %Tesla.Env{status: 500, body: "bad request"}
3972 %{method: :get, url: ^url200} ->
3976 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3978 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3982 [user: user, other_user: other_user]
3985 clear_config(:suggestions)
3987 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3988 Config.put([:suggestions, :enabled], false)
3992 |> assign(:user, user)
3993 |> get("/api/v1/suggestions")
3994 |> json_response(200)
3999 test "returns error", %{conn: conn, user: user} do
4000 Config.put([:suggestions, :enabled], true)
4001 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4005 |> assign(:user, user)
4006 |> get("/api/v1/suggestions")
4007 |> json_response(500)
4009 assert res == "Something went wrong"
4012 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4013 Config.put([:suggestions, :enabled], true)
4014 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4018 |> assign(:user, user)
4019 |> get("/api/v1/suggestions")
4020 |> json_response(200)
4025 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4026 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4030 "acct" => other_user.ap_id,
4031 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4032 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4033 "id" => other_user.id