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:
157 expires_in = 120 * 60
161 |> post("api/v1/statuses", %{
162 "status" => "oolong",
163 "expires_in" => expires_in
166 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
167 assert activity = Activity.get_by_id(fourth_id)
168 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
170 estimated_expires_at =
171 NaiveDateTime.utc_now()
172 |> NaiveDateTime.add(expires_in)
173 |> NaiveDateTime.truncate(:second)
175 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
176 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
178 assert fourth_response["pleroma"]["expires_at"] ==
179 NaiveDateTime.to_iso8601(expiration.scheduled_at)
182 test "replying to a status", %{conn: conn} do
184 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
188 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
190 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
192 activity = Activity.get_by_id(id)
194 assert activity.data["context"] == replied_to.data["context"]
195 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
198 test "replying to a direct message with visibility other than direct", %{conn: conn} do
200 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
202 Enum.each(["public", "private", "unlisted"], fn visibility ->
205 |> post("/api/v1/statuses", %{
206 "status" => "@#{user.nickname} hey",
207 "in_reply_to_id" => replied_to.id,
208 "visibility" => visibility
211 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
215 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
218 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
220 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
221 assert Activity.get_by_id(id)
224 test "posting a sensitive status", %{conn: conn} do
227 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
229 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
230 assert Activity.get_by_id(id)
233 test "posting a fake status", %{conn: conn} do
236 |> post("/api/v1/statuses", %{
238 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
241 real_status = json_response(real_conn, 200)
244 assert Object.get_by_ap_id(real_status["uri"])
248 |> Map.put("id", nil)
249 |> Map.put("url", nil)
250 |> Map.put("uri", nil)
251 |> Map.put("created_at", nil)
252 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
256 |> post("/api/v1/statuses", %{
258 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
262 fake_status = json_response(fake_conn, 200)
265 refute Object.get_by_ap_id(fake_status["uri"])
269 |> Map.put("id", nil)
270 |> Map.put("url", nil)
271 |> Map.put("uri", nil)
272 |> Map.put("created_at", nil)
273 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
275 assert real_status == fake_status
278 test "posting a status with OGP link preview", %{conn: conn} do
279 Config.put([:rich_media, :enabled], true)
283 |> post("/api/v1/statuses", %{
284 "status" => "https://example.com/ogp"
287 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
288 assert Activity.get_by_id(id)
291 test "posting a direct status", %{conn: conn} do
292 user2 = insert(:user)
293 content = "direct cofe @#{user2.nickname}"
297 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
299 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
300 assert activity = Activity.get_by_id(id)
301 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
302 assert activity.data["to"] == [user2.ap_id]
303 assert activity.data["cc"] == []
307 describe "posting polls" do
308 test "posting a poll", %{conn: conn} do
310 time = NaiveDateTime.utc_now()
314 |> assign(:user, user)
315 |> post("/api/v1/statuses", %{
316 "status" => "Who is the #bestgrill?",
317 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
320 response = json_response(conn, 200)
322 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
323 title in ["Rei", "Asuka", "Misato"]
326 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
327 refute response["poll"]["expred"]
330 test "option limit is enforced", %{conn: conn} do
332 limit = Config.get([:instance, :poll_limits, :max_options])
336 |> assign(:user, user)
337 |> post("/api/v1/statuses", %{
339 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
342 %{"error" => error} = json_response(conn, 422)
343 assert error == "Poll can't contain more than #{limit} options"
346 test "option character limit is enforced", %{conn: conn} do
348 limit = Config.get([:instance, :poll_limits, :max_option_chars])
352 |> assign(:user, user)
353 |> post("/api/v1/statuses", %{
356 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
361 %{"error" => error} = json_response(conn, 422)
362 assert error == "Poll options cannot be longer than #{limit} characters each"
365 test "minimal date limit is enforced", %{conn: conn} do
367 limit = Config.get([:instance, :poll_limits, :min_expiration])
371 |> assign(:user, user)
372 |> post("/api/v1/statuses", %{
373 "status" => "imagine arbitrary limits",
375 "options" => ["this post was made by pleroma gang"],
376 "expires_in" => limit - 1
380 %{"error" => error} = json_response(conn, 422)
381 assert error == "Expiration date is too soon"
384 test "maximum date limit is enforced", %{conn: conn} do
386 limit = Config.get([:instance, :poll_limits, :max_expiration])
390 |> assign(:user, user)
391 |> post("/api/v1/statuses", %{
392 "status" => "imagine arbitrary limits",
394 "options" => ["this post was made by pleroma gang"],
395 "expires_in" => limit + 1
399 %{"error" => error} = json_response(conn, 422)
400 assert error == "Expiration date is too far in the future"
404 test "direct timeline", %{conn: conn} do
405 user_one = insert(:user)
406 user_two = insert(:user)
408 {:ok, user_two} = User.follow(user_two, user_one)
411 CommonAPI.post(user_one, %{
412 "status" => "Hi @#{user_two.nickname}!",
413 "visibility" => "direct"
416 {:ok, _follower_only} =
417 CommonAPI.post(user_one, %{
418 "status" => "Hi @#{user_two.nickname}!",
419 "visibility" => "private"
422 # Only direct should be visible here
425 |> assign(:user, user_two)
426 |> get("api/v1/timelines/direct")
428 [status] = json_response(res_conn, 200)
430 assert %{"visibility" => "direct"} = status
431 assert status["url"] != direct.data["id"]
433 # User should be able to see their own direct message
436 |> assign(:user, user_one)
437 |> get("api/v1/timelines/direct")
439 [status] = json_response(res_conn, 200)
441 assert %{"visibility" => "direct"} = status
443 # Both should be visible here
446 |> assign(:user, user_two)
447 |> get("api/v1/timelines/home")
449 [_s1, _s2] = json_response(res_conn, 200)
452 Enum.each(1..20, fn _ ->
454 CommonAPI.post(user_one, %{
455 "status" => "Hi @#{user_two.nickname}!",
456 "visibility" => "direct"
462 |> assign(:user, user_two)
463 |> get("api/v1/timelines/direct")
465 statuses = json_response(res_conn, 200)
466 assert length(statuses) == 20
470 |> assign(:user, user_two)
471 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
473 [status] = json_response(res_conn, 200)
475 assert status["url"] != direct.data["id"]
478 test "Conversations", %{conn: conn} do
479 user_one = insert(:user)
480 user_two = insert(:user)
481 user_three = insert(:user)
483 {:ok, user_two} = User.follow(user_two, user_one)
486 CommonAPI.post(user_one, %{
487 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
488 "visibility" => "direct"
491 {:ok, _follower_only} =
492 CommonAPI.post(user_one, %{
493 "status" => "Hi @#{user_two.nickname}!",
494 "visibility" => "private"
499 |> assign(:user, user_one)
500 |> get("/api/v1/conversations")
502 assert response = json_response(res_conn, 200)
507 "accounts" => res_accounts,
508 "last_status" => res_last_status,
513 account_ids = Enum.map(res_accounts, & &1["id"])
514 assert length(res_accounts) == 2
515 assert user_two.id in account_ids
516 assert user_three.id in account_ids
517 assert is_binary(res_id)
518 assert unread == true
519 assert res_last_status["id"] == direct.id
521 # Apparently undocumented API endpoint
524 |> assign(:user, user_one)
525 |> post("/api/v1/conversations/#{res_id}/read")
527 assert response = json_response(res_conn, 200)
528 assert length(response["accounts"]) == 2
529 assert response["last_status"]["id"] == direct.id
530 assert response["unread"] == false
532 # (vanilla) Mastodon frontend behaviour
535 |> assign(:user, user_one)
536 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
538 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
541 test "doesn't include DMs from blocked users", %{conn: conn} do
542 blocker = insert(:user)
543 blocked = insert(:user)
545 {:ok, blocker} = User.block(blocker, blocked)
547 {:ok, _blocked_direct} =
548 CommonAPI.post(blocked, %{
549 "status" => "Hi @#{blocker.nickname}!",
550 "visibility" => "direct"
554 CommonAPI.post(user, %{
555 "status" => "Hi @#{blocker.nickname}!",
556 "visibility" => "direct"
561 |> assign(:user, user)
562 |> get("api/v1/timelines/direct")
564 [status] = json_response(res_conn, 200)
565 assert status["id"] == direct.id
568 test "verify_credentials", %{conn: conn} do
573 |> assign(:user, user)
574 |> get("/api/v1/accounts/verify_credentials")
576 response = json_response(conn, 200)
578 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
579 assert response["pleroma"]["chat_token"]
580 assert id == to_string(user.id)
583 test "verify_credentials default scope unlisted", %{conn: conn} do
584 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
588 |> assign(:user, user)
589 |> get("/api/v1/accounts/verify_credentials")
591 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
592 assert id == to_string(user.id)
595 test "apps/verify_credentials", %{conn: conn} do
596 token = insert(:oauth_token)
600 |> assign(:user, token.user)
601 |> assign(:token, token)
602 |> get("/api/v1/apps/verify_credentials")
604 app = Repo.preload(token, :app).app
607 "name" => app.client_name,
608 "website" => app.website,
609 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
612 assert expected == json_response(conn, 200)
615 test "user avatar can be set", %{conn: conn} do
617 avatar_image = File.read!("test/fixtures/avatar_data_uri")
621 |> assign(:user, user)
622 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
624 user = refresh_record(user)
638 assert %{"url" => _} = json_response(conn, 200)
641 test "user avatar can be reset", %{conn: conn} do
646 |> assign(:user, user)
647 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
649 user = User.get_cached_by_id(user.id)
651 assert user.avatar == nil
653 assert %{"url" => nil} = json_response(conn, 200)
656 test "can set profile banner", %{conn: conn} do
661 |> assign(:user, user)
662 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
664 user = refresh_record(user)
665 assert user.info.banner["type"] == "Image"
667 assert %{"url" => _} = json_response(conn, 200)
670 test "can reset profile banner", %{conn: conn} do
675 |> assign(:user, user)
676 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
678 user = refresh_record(user)
679 assert user.info.banner == %{}
681 assert %{"url" => nil} = json_response(conn, 200)
684 test "background image can be set", %{conn: conn} do
689 |> assign(:user, user)
690 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
692 user = refresh_record(user)
693 assert user.info.background["type"] == "Image"
694 assert %{"url" => _} = json_response(conn, 200)
697 test "background image can be reset", %{conn: conn} do
702 |> assign(:user, user)
703 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
705 user = refresh_record(user)
706 assert user.info.background == %{}
707 assert %{"url" => nil} = json_response(conn, 200)
710 test "creates an oauth app", %{conn: conn} do
712 app_attrs = build(:oauth_app)
716 |> assign(:user, user)
717 |> post("/api/v1/apps", %{
718 client_name: app_attrs.client_name,
719 redirect_uris: app_attrs.redirect_uris
722 [app] = Repo.all(App)
725 "name" => app.client_name,
726 "website" => app.website,
727 "client_id" => app.client_id,
728 "client_secret" => app.client_secret,
729 "id" => app.id |> to_string(),
730 "redirect_uri" => app.redirect_uris,
731 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
734 assert expected == json_response(conn, 200)
737 test "get a status", %{conn: conn} do
738 activity = insert(:note_activity)
742 |> get("/api/v1/statuses/#{activity.id}")
744 assert %{"id" => id} = json_response(conn, 200)
745 assert id == to_string(activity.id)
748 describe "deleting a status" do
749 test "when you created it", %{conn: conn} do
750 activity = insert(:note_activity)
751 author = User.get_cached_by_ap_id(activity.data["actor"])
755 |> assign(:user, author)
756 |> delete("/api/v1/statuses/#{activity.id}")
758 assert %{} = json_response(conn, 200)
760 refute Activity.get_by_id(activity.id)
763 test "when you didn't create it", %{conn: conn} do
764 activity = insert(:note_activity)
769 |> assign(:user, user)
770 |> delete("/api/v1/statuses/#{activity.id}")
772 assert %{"error" => _} = json_response(conn, 403)
774 assert Activity.get_by_id(activity.id) == activity
777 test "when you're an admin or moderator", %{conn: conn} do
778 activity1 = insert(:note_activity)
779 activity2 = insert(:note_activity)
780 admin = insert(:user, info: %{is_admin: true})
781 moderator = insert(:user, info: %{is_moderator: true})
785 |> assign(:user, admin)
786 |> delete("/api/v1/statuses/#{activity1.id}")
788 assert %{} = json_response(res_conn, 200)
792 |> assign(:user, moderator)
793 |> delete("/api/v1/statuses/#{activity2.id}")
795 assert %{} = json_response(res_conn, 200)
797 refute Activity.get_by_id(activity1.id)
798 refute Activity.get_by_id(activity2.id)
802 describe "filters" do
803 test "creating a filter", %{conn: conn} do
806 filter = %Pleroma.Filter{
813 |> assign(:user, user)
814 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
816 assert response = json_response(conn, 200)
817 assert response["phrase"] == filter.phrase
818 assert response["context"] == filter.context
819 assert response["irreversible"] == false
820 assert response["id"] != nil
821 assert response["id"] != ""
824 test "fetching a list of filters", %{conn: conn} do
827 query_one = %Pleroma.Filter{
834 query_two = %Pleroma.Filter{
841 {:ok, filter_one} = Pleroma.Filter.create(query_one)
842 {:ok, filter_two} = Pleroma.Filter.create(query_two)
846 |> assign(:user, user)
847 |> get("/api/v1/filters")
848 |> json_response(200)
854 filters: [filter_two, filter_one]
858 test "get a filter", %{conn: conn} do
861 query = %Pleroma.Filter{
868 {:ok, filter} = Pleroma.Filter.create(query)
872 |> assign(:user, user)
873 |> get("/api/v1/filters/#{filter.filter_id}")
875 assert _response = json_response(conn, 200)
878 test "update a filter", %{conn: conn} do
881 query = %Pleroma.Filter{
888 {:ok, _filter} = Pleroma.Filter.create(query)
890 new = %Pleroma.Filter{
897 |> assign(:user, user)
898 |> put("/api/v1/filters/#{query.filter_id}", %{
903 assert response = json_response(conn, 200)
904 assert response["phrase"] == new.phrase
905 assert response["context"] == new.context
908 test "delete a filter", %{conn: conn} do
911 query = %Pleroma.Filter{
918 {:ok, filter} = Pleroma.Filter.create(query)
922 |> assign(:user, user)
923 |> delete("/api/v1/filters/#{filter.filter_id}")
925 assert response = json_response(conn, 200)
926 assert response == %{}
931 test "creating a list", %{conn: conn} do
936 |> assign(:user, user)
937 |> post("/api/v1/lists", %{"title" => "cuties"})
939 assert %{"title" => title} = json_response(conn, 200)
940 assert title == "cuties"
943 test "adding users to a list", %{conn: conn} do
945 other_user = insert(:user)
946 {:ok, list} = Pleroma.List.create("name", user)
950 |> assign(:user, user)
951 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
953 assert %{} == json_response(conn, 200)
954 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
955 assert following == [other_user.follower_address]
958 test "removing users from a list", %{conn: conn} do
960 other_user = insert(:user)
961 third_user = insert(:user)
962 {:ok, list} = Pleroma.List.create("name", user)
963 {:ok, list} = Pleroma.List.follow(list, other_user)
964 {:ok, list} = Pleroma.List.follow(list, third_user)
968 |> assign(:user, user)
969 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
971 assert %{} == json_response(conn, 200)
972 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
973 assert following == [third_user.follower_address]
976 test "listing users in a list", %{conn: conn} do
978 other_user = insert(:user)
979 {:ok, list} = Pleroma.List.create("name", user)
980 {:ok, list} = Pleroma.List.follow(list, other_user)
984 |> assign(:user, user)
985 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
987 assert [%{"id" => id}] = json_response(conn, 200)
988 assert id == to_string(other_user.id)
991 test "retrieving a list", %{conn: conn} do
993 {:ok, list} = Pleroma.List.create("name", user)
997 |> assign(:user, user)
998 |> get("/api/v1/lists/#{list.id}")
1000 assert %{"id" => id} = json_response(conn, 200)
1001 assert id == to_string(list.id)
1004 test "renaming a list", %{conn: conn} do
1005 user = insert(:user)
1006 {:ok, list} = Pleroma.List.create("name", user)
1010 |> assign(:user, user)
1011 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
1013 assert %{"title" => name} = json_response(conn, 200)
1014 assert name == "newname"
1017 test "deleting a list", %{conn: conn} do
1018 user = insert(:user)
1019 {:ok, list} = Pleroma.List.create("name", user)
1023 |> assign(:user, user)
1024 |> delete("/api/v1/lists/#{list.id}")
1026 assert %{} = json_response(conn, 200)
1027 assert is_nil(Repo.get(Pleroma.List, list.id))
1030 test "list timeline", %{conn: conn} do
1031 user = insert(:user)
1032 other_user = insert(:user)
1033 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
1034 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1035 {:ok, list} = Pleroma.List.create("name", user)
1036 {:ok, list} = Pleroma.List.follow(list, other_user)
1040 |> assign(:user, user)
1041 |> get("/api/v1/timelines/list/#{list.id}")
1043 assert [%{"id" => id}] = json_response(conn, 200)
1045 assert id == to_string(activity_two.id)
1048 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1049 user = insert(:user)
1050 other_user = insert(:user)
1051 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1053 {:ok, _activity_two} =
1054 CommonAPI.post(other_user, %{
1055 "status" => "Marisa is cute.",
1056 "visibility" => "private"
1059 {:ok, list} = Pleroma.List.create("name", user)
1060 {:ok, list} = Pleroma.List.follow(list, other_user)
1064 |> assign(:user, user)
1065 |> get("/api/v1/timelines/list/#{list.id}")
1067 assert [%{"id" => id}] = json_response(conn, 200)
1069 assert id == to_string(activity_one.id)
1073 describe "notifications" do
1074 test "list of notifications", %{conn: conn} do
1075 user = insert(:user)
1076 other_user = insert(:user)
1078 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1080 {:ok, [_notification]} = Notification.create_notifications(activity)
1084 |> assign(:user, user)
1085 |> get("/api/v1/notifications")
1088 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1090 }\">@<span>#{user.nickname}</span></a></span>"
1092 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1093 assert response == expected_response
1096 test "getting a single notification", %{conn: conn} do
1097 user = insert(:user)
1098 other_user = insert(:user)
1100 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1102 {:ok, [notification]} = Notification.create_notifications(activity)
1106 |> assign(:user, user)
1107 |> get("/api/v1/notifications/#{notification.id}")
1110 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1112 }\">@<span>#{user.nickname}</span></a></span>"
1114 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1115 assert response == expected_response
1118 test "dismissing a single notification", %{conn: conn} do
1119 user = insert(:user)
1120 other_user = insert(:user)
1122 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1124 {:ok, [notification]} = Notification.create_notifications(activity)
1128 |> assign(:user, user)
1129 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1131 assert %{} = json_response(conn, 200)
1134 test "clearing all notifications", %{conn: conn} do
1135 user = insert(:user)
1136 other_user = insert(:user)
1138 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1140 {:ok, [_notification]} = Notification.create_notifications(activity)
1144 |> assign(:user, user)
1145 |> post("/api/v1/notifications/clear")
1147 assert %{} = json_response(conn, 200)
1151 |> assign(:user, user)
1152 |> get("/api/v1/notifications")
1154 assert all = json_response(conn, 200)
1158 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1159 user = insert(:user)
1160 other_user = insert(:user)
1162 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1163 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1164 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1165 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1167 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1168 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1169 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1170 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1174 |> assign(:user, user)
1179 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1181 result = json_response(conn_res, 200)
1182 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1187 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1189 result = json_response(conn_res, 200)
1190 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1195 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1197 result = json_response(conn_res, 200)
1198 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1201 test "filters notifications using exclude_types", %{conn: conn} do
1202 user = insert(:user)
1203 other_user = insert(:user)
1205 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1206 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1207 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1208 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1209 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1211 mention_notification_id =
1212 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1214 favorite_notification_id =
1215 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1217 reblog_notification_id =
1218 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1220 follow_notification_id =
1221 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1225 |> assign(:user, user)
1228 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1230 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1233 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1235 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1238 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1240 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1243 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1245 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1248 test "destroy multiple", %{conn: conn} do
1249 user = insert(:user)
1250 other_user = insert(:user)
1252 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1253 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1254 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1255 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1257 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1258 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1259 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1260 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1264 |> assign(:user, user)
1268 |> get("/api/v1/notifications")
1270 result = json_response(conn_res, 200)
1271 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1275 |> assign(:user, other_user)
1279 |> get("/api/v1/notifications")
1281 result = json_response(conn_res, 200)
1282 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1286 |> delete("/api/v1/notifications/destroy_multiple", %{
1287 "ids" => [notification1_id, notification2_id]
1290 assert json_response(conn_destroy, 200) == %{}
1294 |> get("/api/v1/notifications")
1296 result = json_response(conn_res, 200)
1297 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1300 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1301 user = insert(:user)
1302 user2 = insert(:user)
1304 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1305 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1307 conn = assign(conn, :user, user)
1309 conn = get(conn, "/api/v1/notifications")
1311 assert length(json_response(conn, 200)) == 1
1313 {:ok, user} = User.mute(user, user2)
1315 conn = assign(build_conn(), :user, user)
1316 conn = get(conn, "/api/v1/notifications")
1318 assert json_response(conn, 200) == []
1321 test "see notifications after muting user without notifications", %{conn: conn} do
1322 user = insert(:user)
1323 user2 = insert(:user)
1325 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1326 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1328 conn = assign(conn, :user, user)
1330 conn = get(conn, "/api/v1/notifications")
1332 assert length(json_response(conn, 200)) == 1
1334 {:ok, user} = User.mute(user, user2, false)
1336 conn = assign(build_conn(), :user, user)
1337 conn = get(conn, "/api/v1/notifications")
1339 assert length(json_response(conn, 200)) == 1
1342 test "see notifications after muting user with notifications and with_muted parameter", %{
1345 user = insert(:user)
1346 user2 = insert(:user)
1348 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1349 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1351 conn = assign(conn, :user, user)
1353 conn = get(conn, "/api/v1/notifications")
1355 assert length(json_response(conn, 200)) == 1
1357 {:ok, user} = User.mute(user, user2)
1359 conn = assign(build_conn(), :user, user)
1360 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1362 assert length(json_response(conn, 200)) == 1
1366 describe "reblogging" do
1367 test "reblogs and returns the reblogged status", %{conn: conn} do
1368 activity = insert(:note_activity)
1369 user = insert(:user)
1373 |> assign(:user, user)
1374 |> post("/api/v1/statuses/#{activity.id}/reblog")
1377 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1379 } = json_response(conn, 200)
1381 assert to_string(activity.id) == id
1384 test "reblogged status for another user", %{conn: conn} do
1385 activity = insert(:note_activity)
1386 user1 = insert(:user)
1387 user2 = insert(:user)
1388 user3 = insert(:user)
1389 CommonAPI.favorite(activity.id, user2)
1390 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1391 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1392 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1396 |> assign(:user, user3)
1397 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1400 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1401 "reblogged" => false,
1402 "favourited" => false,
1403 "bookmarked" => false
1404 } = json_response(conn_res, 200)
1408 |> assign(:user, user2)
1409 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1412 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1413 "reblogged" => true,
1414 "favourited" => true,
1415 "bookmarked" => true
1416 } = json_response(conn_res, 200)
1418 assert to_string(activity.id) == id
1421 test "returns 400 error when activity is not exist", %{conn: conn} do
1422 user = insert(:user)
1426 |> assign(:user, user)
1427 |> post("/api/v1/statuses/foo/reblog")
1429 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1433 describe "unreblogging" do
1434 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1435 activity = insert(:note_activity)
1436 user = insert(:user)
1438 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1442 |> assign(:user, user)
1443 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1445 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1447 assert to_string(activity.id) == id
1450 test "returns 400 error when activity is not exist", %{conn: conn} do
1451 user = insert(:user)
1455 |> assign(:user, user)
1456 |> post("/api/v1/statuses/foo/unreblog")
1458 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1462 describe "favoriting" do
1463 test "favs a status and returns it", %{conn: conn} do
1464 activity = insert(:note_activity)
1465 user = insert(:user)
1469 |> assign(:user, user)
1470 |> post("/api/v1/statuses/#{activity.id}/favourite")
1472 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1473 json_response(conn, 200)
1475 assert to_string(activity.id) == id
1478 test "returns 400 error for a wrong id", %{conn: conn} do
1479 user = insert(:user)
1483 |> assign(:user, user)
1484 |> post("/api/v1/statuses/1/favourite")
1486 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1490 describe "unfavoriting" do
1491 test "unfavorites a status and returns it", %{conn: conn} do
1492 activity = insert(:note_activity)
1493 user = insert(:user)
1495 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1499 |> assign(:user, user)
1500 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1502 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1503 json_response(conn, 200)
1505 assert to_string(activity.id) == id
1508 test "returns 400 error for a wrong id", %{conn: conn} do
1509 user = insert(:user)
1513 |> assign(:user, user)
1514 |> post("/api/v1/statuses/1/unfavourite")
1516 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1520 describe "user timelines" do
1521 test "gets a users statuses", %{conn: conn} do
1522 user_one = insert(:user)
1523 user_two = insert(:user)
1524 user_three = insert(:user)
1526 {:ok, user_three} = User.follow(user_three, user_one)
1528 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1530 {:ok, direct_activity} =
1531 CommonAPI.post(user_one, %{
1532 "status" => "Hi, @#{user_two.nickname}.",
1533 "visibility" => "direct"
1536 {:ok, private_activity} =
1537 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1541 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1543 assert [%{"id" => id}] = json_response(resp, 200)
1544 assert id == to_string(activity.id)
1548 |> assign(:user, user_two)
1549 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1551 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1552 assert id_one == to_string(direct_activity.id)
1553 assert id_two == to_string(activity.id)
1557 |> assign(:user, user_three)
1558 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1560 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1561 assert id_one == to_string(private_activity.id)
1562 assert id_two == to_string(activity.id)
1565 test "unimplemented pinned statuses feature", %{conn: conn} do
1566 note = insert(:note_activity)
1567 user = User.get_cached_by_ap_id(note.data["actor"])
1571 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1573 assert json_response(conn, 200) == []
1576 test "gets an users media", %{conn: conn} do
1577 note = insert(:note_activity)
1578 user = User.get_cached_by_ap_id(note.data["actor"])
1580 file = %Plug.Upload{
1581 content_type: "image/jpg",
1582 path: Path.absname("test/fixtures/image.jpg"),
1583 filename: "an_image.jpg"
1587 TwitterAPI.upload(file, user, "json")
1591 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1595 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1597 assert [%{"id" => id}] = json_response(conn, 200)
1598 assert id == to_string(image_post.id)
1602 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1604 assert [%{"id" => id}] = json_response(conn, 200)
1605 assert id == to_string(image_post.id)
1608 test "gets a user's statuses without reblogs", %{conn: conn} do
1609 user = insert(:user)
1610 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1611 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1615 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1617 assert [%{"id" => id}] = json_response(conn, 200)
1618 assert id == to_string(post.id)
1622 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1624 assert [%{"id" => id}] = json_response(conn, 200)
1625 assert id == to_string(post.id)
1628 test "filters user's statuses by a hashtag", %{conn: conn} do
1629 user = insert(:user)
1630 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1631 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1635 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1637 assert [%{"id" => id}] = json_response(conn, 200)
1638 assert id == to_string(post.id)
1642 describe "user relationships" do
1643 test "returns the relationships for the current user", %{conn: conn} do
1644 user = insert(:user)
1645 other_user = insert(:user)
1646 {:ok, user} = User.follow(user, other_user)
1650 |> assign(:user, user)
1651 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1653 assert [relationship] = json_response(conn, 200)
1655 assert to_string(other_user.id) == relationship["id"]
1659 describe "media upload" do
1661 user = insert(:user)
1665 |> assign(:user, user)
1667 image = %Plug.Upload{
1668 content_type: "image/jpg",
1669 path: Path.absname("test/fixtures/image.jpg"),
1670 filename: "an_image.jpg"
1673 [conn: conn, image: image]
1676 clear_config([:media_proxy])
1677 clear_config([Pleroma.Upload])
1679 test "returns uploaded image", %{conn: conn, image: image} do
1680 desc = "Description of the image"
1684 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1685 |> json_response(:ok)
1687 assert media["type"] == "image"
1688 assert media["description"] == desc
1691 object = Repo.get(Object, media["id"])
1692 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1696 describe "locked accounts" do
1697 test "/api/v1/follow_requests works" do
1698 user = insert(:user, %{info: %User.Info{locked: true}})
1699 other_user = insert(:user)
1701 {:ok, _activity} = ActivityPub.follow(other_user, user)
1703 user = User.get_cached_by_id(user.id)
1704 other_user = User.get_cached_by_id(other_user.id)
1706 assert User.following?(other_user, user) == false
1710 |> assign(:user, user)
1711 |> get("/api/v1/follow_requests")
1713 assert [relationship] = json_response(conn, 200)
1714 assert to_string(other_user.id) == relationship["id"]
1717 test "/api/v1/follow_requests/:id/authorize works" do
1718 user = insert(:user, %{info: %User.Info{locked: true}})
1719 other_user = insert(:user)
1721 {:ok, _activity} = ActivityPub.follow(other_user, user)
1723 user = User.get_cached_by_id(user.id)
1724 other_user = User.get_cached_by_id(other_user.id)
1726 assert User.following?(other_user, user) == false
1730 |> assign(:user, user)
1731 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1733 assert relationship = json_response(conn, 200)
1734 assert to_string(other_user.id) == relationship["id"]
1736 user = User.get_cached_by_id(user.id)
1737 other_user = User.get_cached_by_id(other_user.id)
1739 assert User.following?(other_user, user) == true
1742 test "verify_credentials", %{conn: conn} do
1743 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1747 |> assign(:user, user)
1748 |> get("/api/v1/accounts/verify_credentials")
1750 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1751 assert id == to_string(user.id)
1754 test "/api/v1/follow_requests/:id/reject works" do
1755 user = insert(:user, %{info: %User.Info{locked: true}})
1756 other_user = insert(:user)
1758 {:ok, _activity} = ActivityPub.follow(other_user, user)
1760 user = User.get_cached_by_id(user.id)
1764 |> assign(:user, user)
1765 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1767 assert relationship = json_response(conn, 200)
1768 assert to_string(other_user.id) == relationship["id"]
1770 user = User.get_cached_by_id(user.id)
1771 other_user = User.get_cached_by_id(other_user.id)
1773 assert User.following?(other_user, user) == false
1777 test "account fetching", %{conn: conn} do
1778 user = insert(:user)
1782 |> get("/api/v1/accounts/#{user.id}")
1784 assert %{"id" => id} = json_response(conn, 200)
1785 assert id == to_string(user.id)
1789 |> get("/api/v1/accounts/-1")
1791 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1794 test "account fetching also works nickname", %{conn: conn} do
1795 user = insert(:user)
1799 |> get("/api/v1/accounts/#{user.nickname}")
1801 assert %{"id" => id} = json_response(conn, 200)
1802 assert id == user.id
1805 test "mascot upload", %{conn: conn} do
1806 user = insert(:user)
1808 non_image_file = %Plug.Upload{
1809 content_type: "audio/mpeg",
1810 path: Path.absname("test/fixtures/sound.mp3"),
1811 filename: "sound.mp3"
1816 |> assign(:user, user)
1817 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1819 assert json_response(conn, 415)
1821 file = %Plug.Upload{
1822 content_type: "image/jpg",
1823 path: Path.absname("test/fixtures/image.jpg"),
1824 filename: "an_image.jpg"
1829 |> assign(:user, user)
1830 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1832 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1835 test "mascot retrieving", %{conn: conn} do
1836 user = insert(:user)
1837 # When user hasn't set a mascot, we should just get pleroma tan back
1840 |> assign(:user, user)
1841 |> get("/api/v1/pleroma/mascot")
1843 assert %{"url" => url} = json_response(conn, 200)
1844 assert url =~ "pleroma-fox-tan-smol"
1846 # When a user sets their mascot, we should get that back
1847 file = %Plug.Upload{
1848 content_type: "image/jpg",
1849 path: Path.absname("test/fixtures/image.jpg"),
1850 filename: "an_image.jpg"
1855 |> assign(:user, user)
1856 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1858 assert json_response(conn, 200)
1860 user = User.get_cached_by_id(user.id)
1864 |> assign(:user, user)
1865 |> get("/api/v1/pleroma/mascot")
1867 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1868 assert url =~ "an_image"
1871 test "hashtag timeline", %{conn: conn} do
1872 following = insert(:user)
1875 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1877 {:ok, [_activity]} =
1878 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1882 |> get("/api/v1/timelines/tag/2hu")
1884 assert [%{"id" => id}] = json_response(nconn, 200)
1886 assert id == to_string(activity.id)
1888 # works for different capitalization too
1891 |> get("/api/v1/timelines/tag/2HU")
1893 assert [%{"id" => id}] = json_response(nconn, 200)
1895 assert id == to_string(activity.id)
1899 test "multi-hashtag timeline", %{conn: conn} do
1900 user = insert(:user)
1902 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1903 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1904 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1908 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1910 [status_none, status_test1, status_test] = json_response(any_test, 200)
1912 assert to_string(activity_test.id) == status_test["id"]
1913 assert to_string(activity_test1.id) == status_test1["id"]
1914 assert to_string(activity_none.id) == status_none["id"]
1918 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1920 assert [status_test1] == json_response(restricted_test, 200)
1922 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1924 assert [status_none] == json_response(all_test, 200)
1927 test "getting followers", %{conn: conn} do
1928 user = insert(:user)
1929 other_user = insert(:user)
1930 {:ok, user} = User.follow(user, other_user)
1934 |> get("/api/v1/accounts/#{other_user.id}/followers")
1936 assert [%{"id" => id}] = json_response(conn, 200)
1937 assert id == to_string(user.id)
1940 test "getting followers, hide_followers", %{conn: conn} do
1941 user = insert(:user)
1942 other_user = insert(:user, %{info: %{hide_followers: true}})
1943 {:ok, _user} = User.follow(user, other_user)
1947 |> get("/api/v1/accounts/#{other_user.id}/followers")
1949 assert [] == json_response(conn, 200)
1952 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1953 user = insert(:user)
1954 other_user = insert(:user, %{info: %{hide_followers: true}})
1955 {:ok, _user} = User.follow(user, other_user)
1959 |> assign(:user, other_user)
1960 |> get("/api/v1/accounts/#{other_user.id}/followers")
1962 refute [] == json_response(conn, 200)
1965 test "getting followers, pagination", %{conn: conn} do
1966 user = insert(:user)
1967 follower1 = insert(:user)
1968 follower2 = insert(:user)
1969 follower3 = insert(:user)
1970 {:ok, _} = User.follow(follower1, user)
1971 {:ok, _} = User.follow(follower2, user)
1972 {:ok, _} = User.follow(follower3, user)
1976 |> assign(:user, user)
1980 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1982 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1983 assert id3 == follower3.id
1984 assert id2 == follower2.id
1988 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1990 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1991 assert id2 == follower2.id
1992 assert id1 == follower1.id
1996 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1998 assert [%{"id" => id2}] = json_response(res_conn, 200)
1999 assert id2 == follower2.id
2001 assert [link_header] = get_resp_header(res_conn, "link")
2002 assert link_header =~ ~r/min_id=#{follower2.id}/
2003 assert link_header =~ ~r/max_id=#{follower2.id}/
2006 test "getting following", %{conn: conn} do
2007 user = insert(:user)
2008 other_user = insert(:user)
2009 {:ok, user} = User.follow(user, other_user)
2013 |> get("/api/v1/accounts/#{user.id}/following")
2015 assert [%{"id" => id}] = json_response(conn, 200)
2016 assert id == to_string(other_user.id)
2019 test "getting following, hide_follows", %{conn: conn} do
2020 user = insert(:user, %{info: %{hide_follows: true}})
2021 other_user = insert(:user)
2022 {:ok, user} = User.follow(user, other_user)
2026 |> get("/api/v1/accounts/#{user.id}/following")
2028 assert [] == json_response(conn, 200)
2031 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2032 user = insert(:user, %{info: %{hide_follows: true}})
2033 other_user = insert(:user)
2034 {:ok, user} = User.follow(user, other_user)
2038 |> assign(:user, user)
2039 |> get("/api/v1/accounts/#{user.id}/following")
2041 refute [] == json_response(conn, 200)
2044 test "getting following, pagination", %{conn: conn} do
2045 user = insert(:user)
2046 following1 = insert(:user)
2047 following2 = insert(:user)
2048 following3 = insert(:user)
2049 {:ok, _} = User.follow(user, following1)
2050 {:ok, _} = User.follow(user, following2)
2051 {:ok, _} = User.follow(user, following3)
2055 |> assign(:user, user)
2059 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2061 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2062 assert id3 == following3.id
2063 assert id2 == following2.id
2067 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2069 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2070 assert id2 == following2.id
2071 assert id1 == following1.id
2075 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2077 assert [%{"id" => id2}] = json_response(res_conn, 200)
2078 assert id2 == following2.id
2080 assert [link_header] = get_resp_header(res_conn, "link")
2081 assert link_header =~ ~r/min_id=#{following2.id}/
2082 assert link_header =~ ~r/max_id=#{following2.id}/
2085 test "following / unfollowing a user", %{conn: conn} do
2086 user = insert(:user)
2087 other_user = insert(:user)
2091 |> assign(:user, user)
2092 |> post("/api/v1/accounts/#{other_user.id}/follow")
2094 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2096 user = User.get_cached_by_id(user.id)
2100 |> assign(:user, user)
2101 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2103 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2105 user = User.get_cached_by_id(user.id)
2109 |> assign(:user, user)
2110 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2112 assert %{"id" => id} = json_response(conn, 200)
2113 assert id == to_string(other_user.id)
2116 test "following without reblogs" do
2117 follower = insert(:user)
2118 followed = insert(:user)
2119 other_user = insert(:user)
2123 |> assign(:user, follower)
2124 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2126 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2128 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2129 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2133 |> assign(:user, User.get_cached_by_id(follower.id))
2134 |> get("/api/v1/timelines/home")
2136 assert [] == json_response(conn, 200)
2140 |> assign(:user, follower)
2141 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2143 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2147 |> assign(:user, User.get_cached_by_id(follower.id))
2148 |> get("/api/v1/timelines/home")
2150 expected_activity_id = reblog.id
2151 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2154 test "following / unfollowing errors" do
2155 user = insert(:user)
2159 |> assign(:user, user)
2162 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2163 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2166 user = User.get_cached_by_id(user.id)
2167 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2168 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2170 # self follow via uri
2171 user = User.get_cached_by_id(user.id)
2172 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2173 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2175 # follow non existing user
2176 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2177 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2179 # follow non existing user via uri
2180 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2181 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2183 # unfollow non existing user
2184 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2185 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2188 describe "mute/unmute" do
2189 test "with notifications", %{conn: conn} do
2190 user = insert(:user)
2191 other_user = insert(:user)
2195 |> assign(:user, user)
2196 |> post("/api/v1/accounts/#{other_user.id}/mute")
2198 response = json_response(conn, 200)
2200 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2201 user = User.get_cached_by_id(user.id)
2205 |> assign(:user, user)
2206 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2208 response = json_response(conn, 200)
2209 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2212 test "without notifications", %{conn: conn} do
2213 user = insert(:user)
2214 other_user = insert(:user)
2218 |> assign(:user, user)
2219 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2221 response = json_response(conn, 200)
2223 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2224 user = User.get_cached_by_id(user.id)
2228 |> assign(:user, user)
2229 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2231 response = json_response(conn, 200)
2232 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2236 test "subscribing / unsubscribing to a user", %{conn: conn} do
2237 user = insert(:user)
2238 subscription_target = insert(:user)
2242 |> assign(:user, user)
2243 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2245 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2249 |> assign(:user, user)
2250 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2252 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2255 test "getting a list of mutes", %{conn: conn} do
2256 user = insert(:user)
2257 other_user = insert(:user)
2259 {:ok, user} = User.mute(user, other_user)
2263 |> assign(:user, user)
2264 |> get("/api/v1/mutes")
2266 other_user_id = to_string(other_user.id)
2267 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2270 test "blocking / unblocking a user", %{conn: conn} do
2271 user = insert(:user)
2272 other_user = insert(:user)
2276 |> assign(:user, user)
2277 |> post("/api/v1/accounts/#{other_user.id}/block")
2279 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2281 user = User.get_cached_by_id(user.id)
2285 |> assign(:user, user)
2286 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2288 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2291 test "getting a list of blocks", %{conn: conn} do
2292 user = insert(:user)
2293 other_user = insert(:user)
2295 {:ok, user} = User.block(user, other_user)
2299 |> assign(:user, user)
2300 |> get("/api/v1/blocks")
2302 other_user_id = to_string(other_user.id)
2303 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2306 test "blocking / unblocking a domain", %{conn: conn} do
2307 user = insert(:user)
2308 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2312 |> assign(:user, user)
2313 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2315 assert %{} = json_response(conn, 200)
2316 user = User.get_cached_by_ap_id(user.ap_id)
2317 assert User.blocks?(user, other_user)
2321 |> assign(:user, user)
2322 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2324 assert %{} = json_response(conn, 200)
2325 user = User.get_cached_by_ap_id(user.ap_id)
2326 refute User.blocks?(user, other_user)
2329 test "getting a list of domain blocks", %{conn: conn} do
2330 user = insert(:user)
2332 {:ok, user} = User.block_domain(user, "bad.site")
2333 {:ok, user} = User.block_domain(user, "even.worse.site")
2337 |> assign(:user, user)
2338 |> get("/api/v1/domain_blocks")
2340 domain_blocks = json_response(conn, 200)
2342 assert "bad.site" in domain_blocks
2343 assert "even.worse.site" in domain_blocks
2346 test "unimplemented follow_requests, blocks, domain blocks" do
2347 user = insert(:user)
2349 ["blocks", "domain_blocks", "follow_requests"]
2350 |> Enum.each(fn endpoint ->
2353 |> assign(:user, user)
2354 |> get("/api/v1/#{endpoint}")
2356 assert [] = json_response(conn, 200)
2360 test "returns the favorites of a user", %{conn: conn} do
2361 user = insert(:user)
2362 other_user = insert(:user)
2364 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2365 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2367 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2371 |> assign(:user, user)
2372 |> get("/api/v1/favourites")
2374 assert [status] = json_response(first_conn, 200)
2375 assert status["id"] == to_string(activity.id)
2377 assert [{"link", _link_header}] =
2378 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2380 # Honours query params
2381 {:ok, second_activity} =
2382 CommonAPI.post(other_user, %{
2384 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2387 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2389 last_like = status["id"]
2393 |> assign(:user, user)
2394 |> get("/api/v1/favourites?since_id=#{last_like}")
2396 assert [second_status] = json_response(second_conn, 200)
2397 assert second_status["id"] == to_string(second_activity.id)
2401 |> assign(:user, user)
2402 |> get("/api/v1/favourites?limit=0")
2404 assert [] = json_response(third_conn, 200)
2407 describe "getting favorites timeline of specified user" do
2409 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2410 [current_user: current_user, user: user]
2413 test "returns list of statuses favorited by specified user", %{
2415 current_user: current_user,
2418 [activity | _] = insert_pair(:note_activity)
2419 CommonAPI.favorite(activity.id, user)
2423 |> assign(:user, current_user)
2424 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2425 |> json_response(:ok)
2429 assert length(response) == 1
2430 assert like["id"] == activity.id
2433 test "returns favorites for specified user_id when user is not logged in", %{
2437 activity = insert(:note_activity)
2438 CommonAPI.favorite(activity.id, user)
2442 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2443 |> json_response(:ok)
2445 assert length(response) == 1
2448 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2450 current_user: current_user,
2454 CommonAPI.post(current_user, %{
2455 "status" => "Hi @#{user.nickname}!",
2456 "visibility" => "direct"
2459 CommonAPI.favorite(direct.id, user)
2463 |> assign(:user, current_user)
2464 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2465 |> json_response(:ok)
2467 assert length(response) == 1
2469 anonymous_response =
2471 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2472 |> json_response(:ok)
2474 assert Enum.empty?(anonymous_response)
2477 test "does not return others' favorited DM when user is not one of recipients", %{
2479 current_user: current_user,
2482 user_two = insert(:user)
2485 CommonAPI.post(user_two, %{
2486 "status" => "Hi @#{user.nickname}!",
2487 "visibility" => "direct"
2490 CommonAPI.favorite(direct.id, user)
2494 |> assign(:user, current_user)
2495 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2496 |> json_response(:ok)
2498 assert Enum.empty?(response)
2501 test "paginates favorites using since_id and max_id", %{
2503 current_user: current_user,
2506 activities = insert_list(10, :note_activity)
2508 Enum.each(activities, fn activity ->
2509 CommonAPI.favorite(activity.id, user)
2512 third_activity = Enum.at(activities, 2)
2513 seventh_activity = Enum.at(activities, 6)
2517 |> assign(:user, current_user)
2518 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2519 since_id: third_activity.id,
2520 max_id: seventh_activity.id
2522 |> json_response(:ok)
2524 assert length(response) == 3
2525 refute third_activity in response
2526 refute seventh_activity in response
2529 test "limits favorites using limit parameter", %{
2531 current_user: current_user,
2535 |> insert_list(:note_activity)
2536 |> Enum.each(fn activity ->
2537 CommonAPI.favorite(activity.id, user)
2542 |> assign(:user, current_user)
2543 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2544 |> json_response(:ok)
2546 assert length(response) == 3
2549 test "returns empty response when user does not have any favorited statuses", %{
2551 current_user: current_user,
2556 |> assign(:user, current_user)
2557 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2558 |> json_response(:ok)
2560 assert Enum.empty?(response)
2563 test "returns 404 error when specified user is not exist", %{conn: conn} do
2564 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2566 assert json_response(conn, 404) == %{"error" => "Record not found"}
2569 test "returns 403 error when user has hidden own favorites", %{
2571 current_user: current_user
2573 user = insert(:user, %{info: %{hide_favorites: true}})
2574 activity = insert(:note_activity)
2575 CommonAPI.favorite(activity.id, user)
2579 |> assign(:user, current_user)
2580 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2582 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2585 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2586 user = insert(:user)
2587 activity = insert(:note_activity)
2588 CommonAPI.favorite(activity.id, user)
2592 |> assign(:user, current_user)
2593 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2595 assert user.info.hide_favorites
2596 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2600 test "get instance information", %{conn: conn} do
2601 conn = get(conn, "/api/v1/instance")
2602 assert result = json_response(conn, 200)
2604 email = Config.get([:instance, :email])
2605 # Note: not checking for "max_toot_chars" since it's optional
2611 "email" => from_config_email,
2613 "streaming_api" => _
2618 "registrations" => _,
2622 assert email == from_config_email
2625 test "get instance stats", %{conn: conn} do
2626 user = insert(:user, %{local: true})
2628 user2 = insert(:user, %{local: true})
2629 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2631 insert(:user, %{local: false, nickname: "u@peer1.com"})
2632 insert(:user, %{local: false, nickname: "u@peer2.com"})
2634 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2636 # Stats should count users with missing or nil `info.deactivated` value
2637 user = User.get_cached_by_id(user.id)
2638 info_change = Changeset.change(user.info, %{deactivated: nil})
2642 |> Changeset.change()
2643 |> Changeset.put_embed(:info, info_change)
2644 |> User.update_and_set_cache()
2646 Pleroma.Stats.force_update()
2648 conn = get(conn, "/api/v1/instance")
2650 assert result = json_response(conn, 200)
2652 stats = result["stats"]
2655 assert stats["user_count"] == 1
2656 assert stats["status_count"] == 1
2657 assert stats["domain_count"] == 2
2660 test "get peers", %{conn: conn} do
2661 insert(:user, %{local: false, nickname: "u@peer1.com"})
2662 insert(:user, %{local: false, nickname: "u@peer2.com"})
2664 Pleroma.Stats.force_update()
2666 conn = get(conn, "/api/v1/instance/peers")
2668 assert result = json_response(conn, 200)
2670 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2673 test "put settings", %{conn: conn} do
2674 user = insert(:user)
2678 |> assign(:user, user)
2679 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2681 assert _result = json_response(conn, 200)
2683 user = User.get_cached_by_ap_id(user.ap_id)
2684 assert user.info.settings == %{"programming" => "socks"}
2687 describe "pinned statuses" do
2689 user = insert(:user)
2690 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2692 [user: user, activity: activity]
2695 clear_config([:instance, :max_pinned_statuses]) do
2696 Config.put([:instance, :max_pinned_statuses], 1)
2699 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2700 {:ok, _} = CommonAPI.pin(activity.id, user)
2704 |> assign(:user, user)
2705 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2706 |> json_response(200)
2708 id_str = to_string(activity.id)
2710 assert [%{"id" => ^id_str, "pinned" => true}] = result
2713 test "pin status", %{conn: conn, user: user, activity: activity} do
2714 id_str = to_string(activity.id)
2716 assert %{"id" => ^id_str, "pinned" => true} =
2718 |> assign(:user, user)
2719 |> post("/api/v1/statuses/#{activity.id}/pin")
2720 |> json_response(200)
2722 assert [%{"id" => ^id_str, "pinned" => true}] =
2724 |> assign(:user, user)
2725 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2726 |> json_response(200)
2729 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2730 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2734 |> assign(:user, user)
2735 |> post("/api/v1/statuses/#{dm.id}/pin")
2737 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2740 test "unpin status", %{conn: conn, user: user, activity: activity} do
2741 {:ok, _} = CommonAPI.pin(activity.id, user)
2743 id_str = to_string(activity.id)
2744 user = refresh_record(user)
2746 assert %{"id" => ^id_str, "pinned" => false} =
2748 |> assign(:user, user)
2749 |> post("/api/v1/statuses/#{activity.id}/unpin")
2750 |> json_response(200)
2754 |> assign(:user, user)
2755 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2756 |> json_response(200)
2759 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2762 |> assign(:user, user)
2763 |> post("/api/v1/statuses/1/unpin")
2765 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2768 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2769 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2771 id_str_one = to_string(activity_one.id)
2773 assert %{"id" => ^id_str_one, "pinned" => true} =
2775 |> assign(:user, user)
2776 |> post("/api/v1/statuses/#{id_str_one}/pin")
2777 |> json_response(200)
2779 user = refresh_record(user)
2781 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2783 |> assign(:user, user)
2784 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2785 |> json_response(400)
2791 Config.put([:rich_media, :enabled], true)
2793 user = insert(:user)
2797 test "returns rich-media card", %{conn: conn, user: user} do
2798 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2801 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2802 "provider_name" => "example.com",
2803 "provider_url" => "https://example.com",
2804 "title" => "The Rock",
2806 "url" => "https://example.com/ogp",
2808 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2811 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2812 "title" => "The Rock",
2813 "type" => "video.movie",
2814 "url" => "https://example.com/ogp",
2816 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2823 |> get("/api/v1/statuses/#{activity.id}/card")
2824 |> json_response(200)
2826 assert response == card_data
2828 # works with private posts
2830 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2834 |> assign(:user, user)
2835 |> get("/api/v1/statuses/#{activity.id}/card")
2836 |> json_response(200)
2838 assert response_two == card_data
2841 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2843 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2847 |> get("/api/v1/statuses/#{activity.id}/card")
2848 |> json_response(:ok)
2850 assert response == %{
2852 "title" => "Pleroma",
2853 "description" => "",
2855 "provider_name" => "example.com",
2856 "provider_url" => "https://example.com",
2857 "url" => "https://example.com/ogp-missing-data",
2860 "title" => "Pleroma",
2861 "type" => "website",
2862 "url" => "https://example.com/ogp-missing-data"
2870 user = insert(:user)
2871 for_user = insert(:user)
2874 CommonAPI.post(user, %{
2875 "status" => "heweoo?"
2879 CommonAPI.post(user, %{
2880 "status" => "heweoo!"
2885 |> assign(:user, for_user)
2886 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2888 assert json_response(response1, 200)["bookmarked"] == true
2892 |> assign(:user, for_user)
2893 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2895 assert json_response(response2, 200)["bookmarked"] == true
2899 |> assign(:user, for_user)
2900 |> get("/api/v1/bookmarks")
2902 assert [json_response(response2, 200), json_response(response1, 200)] ==
2903 json_response(bookmarks, 200)
2907 |> assign(:user, for_user)
2908 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2910 assert json_response(response1, 200)["bookmarked"] == false
2914 |> assign(:user, for_user)
2915 |> get("/api/v1/bookmarks")
2917 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2920 describe "conversation muting" do
2922 post_user = insert(:user)
2923 user = insert(:user)
2925 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2927 [user: user, activity: activity]
2930 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2931 id_str = to_string(activity.id)
2933 assert %{"id" => ^id_str, "muted" => true} =
2935 |> assign(:user, user)
2936 |> post("/api/v1/statuses/#{activity.id}/mute")
2937 |> json_response(200)
2940 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2941 {:ok, _} = CommonAPI.add_mute(user, activity)
2945 |> assign(:user, user)
2946 |> post("/api/v1/statuses/#{activity.id}/mute")
2948 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2951 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2952 {:ok, _} = CommonAPI.add_mute(user, activity)
2954 id_str = to_string(activity.id)
2955 user = refresh_record(user)
2957 assert %{"id" => ^id_str, "muted" => false} =
2959 |> assign(:user, user)
2960 |> post("/api/v1/statuses/#{activity.id}/unmute")
2961 |> json_response(200)
2965 describe "reports" do
2967 reporter = insert(:user)
2968 target_user = insert(:user)
2970 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2972 [reporter: reporter, target_user: target_user, activity: activity]
2975 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2976 assert %{"action_taken" => false, "id" => _} =
2978 |> assign(:user, reporter)
2979 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2980 |> json_response(200)
2983 test "submit a report with statuses and comment", %{
2986 target_user: target_user,
2989 assert %{"action_taken" => false, "id" => _} =
2991 |> assign(:user, reporter)
2992 |> post("/api/v1/reports", %{
2993 "account_id" => target_user.id,
2994 "status_ids" => [activity.id],
2995 "comment" => "bad status!",
2996 "forward" => "false"
2998 |> json_response(200)
3001 test "account_id is required", %{
3006 assert %{"error" => "Valid `account_id` required"} =
3008 |> assign(:user, reporter)
3009 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3010 |> json_response(400)
3013 test "comment must be up to the size specified in the config", %{
3016 target_user: target_user
3018 max_size = Config.get([:instance, :max_report_comment_size], 1000)
3019 comment = String.pad_trailing("a", max_size + 1, "a")
3021 error = %{"error" => "Comment must be up to #{max_size} characters"}
3025 |> assign(:user, reporter)
3026 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3027 |> json_response(400)
3030 test "returns error when account is not exist", %{
3037 |> assign(:user, reporter)
3038 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3040 assert json_response(conn, 400) == %{"error" => "Account not found"}
3044 describe "link headers" do
3045 test "preserves parameters in link headers", %{conn: conn} do
3046 user = insert(:user)
3047 other_user = insert(:user)
3050 CommonAPI.post(other_user, %{
3051 "status" => "hi @#{user.nickname}",
3052 "visibility" => "public"
3056 CommonAPI.post(other_user, %{
3057 "status" => "hi @#{user.nickname}",
3058 "visibility" => "public"
3061 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3062 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3066 |> assign(:user, user)
3067 |> get("/api/v1/notifications", %{media_only: true})
3069 assert [link_header] = get_resp_header(conn, "link")
3070 assert link_header =~ ~r/media_only=true/
3071 assert link_header =~ ~r/min_id=#{notification2.id}/
3072 assert link_header =~ ~r/max_id=#{notification1.id}/
3076 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3077 # Need to set an old-style integer ID to reproduce the problem
3078 # (these are no longer assigned to new accounts but were preserved
3079 # for existing accounts during the migration to flakeIDs)
3080 user_one = insert(:user, %{id: 1212})
3081 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3085 |> get("/api/v1/accounts/#{user_one.id}")
3089 |> get("/api/v1/accounts/#{user_two.nickname}")
3093 |> get("/api/v1/accounts/#{user_two.id}")
3095 acc_one = json_response(resp_one, 200)
3096 acc_two = json_response(resp_two, 200)
3097 acc_three = json_response(resp_three, 200)
3098 refute acc_one == acc_two
3099 assert acc_two == acc_three
3102 describe "custom emoji" do
3103 test "with tags", %{conn: conn} do
3106 |> get("/api/v1/custom_emojis")
3107 |> json_response(200)
3109 assert Map.has_key?(emoji, "shortcode")
3110 assert Map.has_key?(emoji, "static_url")
3111 assert Map.has_key?(emoji, "tags")
3112 assert is_list(emoji["tags"])
3113 assert Map.has_key?(emoji, "category")
3114 assert Map.has_key?(emoji, "url")
3115 assert Map.has_key?(emoji, "visible_in_picker")
3119 describe "index/2 redirections" do
3120 setup %{conn: conn} do
3124 signing_salt: "cooldude"
3129 |> Plug.Session.call(Plug.Session.init(session_opts))
3132 test_path = "/web/statuses/test"
3133 %{conn: conn, path: test_path}
3136 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3137 conn = get(conn, path)
3139 assert conn.status == 302
3140 assert redirected_to(conn) == "/web/login"
3143 test "redirects not logged-in users to the login page on private instances", %{
3147 Config.put([:instance, :public], false)
3149 conn = get(conn, path)
3151 assert conn.status == 302
3152 assert redirected_to(conn) == "/web/login"
3155 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3156 token = insert(:oauth_token)
3160 |> assign(:user, token.user)
3161 |> put_session(:oauth_token, token.token)
3164 assert conn.status == 200
3167 test "saves referer path to session", %{conn: conn, path: path} do
3168 conn = get(conn, path)
3169 return_to = Plug.Conn.get_session(conn, :return_to)
3171 assert return_to == path
3174 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3175 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3176 auth = insert(:oauth_authorization, app: app)
3180 |> put_session(:return_to, path)
3181 |> get("/web/login", %{code: auth.token})
3183 assert conn.status == 302
3184 assert redirected_to(conn) == path
3187 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3188 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3189 auth = insert(:oauth_authorization, app: app)
3191 conn = get(conn, "/web/login", %{code: auth.token})
3193 assert conn.status == 302
3194 assert redirected_to(conn) == "/web/getting-started"
3198 describe "scheduled activities" do
3199 test "creates a scheduled activity", %{conn: conn} do
3200 user = insert(:user)
3201 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3205 |> assign(:user, user)
3206 |> post("/api/v1/statuses", %{
3207 "status" => "scheduled",
3208 "scheduled_at" => scheduled_at
3211 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3212 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3213 assert [] == Repo.all(Activity)
3216 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3217 user = insert(:user)
3218 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3220 file = %Plug.Upload{
3221 content_type: "image/jpg",
3222 path: Path.absname("test/fixtures/image.jpg"),
3223 filename: "an_image.jpg"
3226 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3230 |> assign(:user, user)
3231 |> post("/api/v1/statuses", %{
3232 "media_ids" => [to_string(upload.id)],
3233 "status" => "scheduled",
3234 "scheduled_at" => scheduled_at
3237 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3238 assert %{"type" => "image"} = media_attachment
3241 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3243 user = insert(:user)
3246 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3250 |> assign(:user, user)
3251 |> post("/api/v1/statuses", %{
3252 "status" => "not scheduled",
3253 "scheduled_at" => scheduled_at
3256 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3257 assert [] == Repo.all(ScheduledActivity)
3260 test "returns error when daily user limit is exceeded", %{conn: conn} do
3261 user = insert(:user)
3264 NaiveDateTime.utc_now()
3265 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3266 |> NaiveDateTime.to_iso8601()
3268 attrs = %{params: %{}, scheduled_at: today}
3269 {:ok, _} = ScheduledActivity.create(user, attrs)
3270 {:ok, _} = ScheduledActivity.create(user, attrs)
3274 |> assign(:user, user)
3275 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3277 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3280 test "returns error when total user limit is exceeded", %{conn: conn} do
3281 user = insert(:user)
3284 NaiveDateTime.utc_now()
3285 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3286 |> NaiveDateTime.to_iso8601()
3289 NaiveDateTime.utc_now()
3290 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3291 |> NaiveDateTime.to_iso8601()
3293 attrs = %{params: %{}, scheduled_at: today}
3294 {:ok, _} = ScheduledActivity.create(user, attrs)
3295 {:ok, _} = ScheduledActivity.create(user, attrs)
3296 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3300 |> assign(:user, user)
3301 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3303 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3306 test "shows scheduled activities", %{conn: conn} do
3307 user = insert(:user)
3308 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3309 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3310 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3311 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3315 |> assign(:user, user)
3320 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3322 result = json_response(conn_res, 200)
3323 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3328 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3330 result = json_response(conn_res, 200)
3331 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3336 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3338 result = json_response(conn_res, 200)
3339 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3342 test "shows a scheduled activity", %{conn: conn} do
3343 user = insert(:user)
3344 scheduled_activity = insert(:scheduled_activity, user: user)
3348 |> assign(:user, user)
3349 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3351 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3352 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3356 |> assign(:user, user)
3357 |> get("/api/v1/scheduled_statuses/404")
3359 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3362 test "updates a scheduled activity", %{conn: conn} do
3363 user = insert(:user)
3364 scheduled_activity = insert(:scheduled_activity, user: user)
3367 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3371 |> assign(:user, user)
3372 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3373 scheduled_at: new_scheduled_at
3376 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3377 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3381 |> assign(:user, user)
3382 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3384 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3387 test "deletes a scheduled activity", %{conn: conn} do
3388 user = insert(:user)
3389 scheduled_activity = insert(:scheduled_activity, user: user)
3393 |> assign(:user, user)
3394 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3396 assert %{} = json_response(res_conn, 200)
3397 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3401 |> assign(:user, user)
3402 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3404 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3408 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3409 user1 = insert(:user)
3410 user2 = insert(:user)
3411 user3 = insert(:user)
3413 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3415 # Reply to status from another user
3418 |> assign(:user, user2)
3419 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3421 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3423 activity = Activity.get_by_id_with_object(id)
3425 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3426 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3428 # Reblog from the third user
3431 |> assign(:user, user3)
3432 |> post("/api/v1/statuses/#{activity.id}/reblog")
3434 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3435 json_response(conn2, 200)
3437 assert to_string(activity.id) == id
3439 # Getting third user status
3442 |> assign(:user, user3)
3443 |> get("api/v1/timelines/home")
3445 [reblogged_activity] = json_response(conn3, 200)
3447 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3449 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3450 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3453 describe "create account by app" do
3454 test "Account registration via Application", %{conn: conn} do
3457 |> post("/api/v1/apps", %{
3458 client_name: "client_name",
3459 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3460 scopes: "read, write, follow"
3464 "client_id" => client_id,
3465 "client_secret" => client_secret,
3467 "name" => "client_name",
3468 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3471 } = json_response(conn, 200)
3475 |> post("/oauth/token", %{
3476 grant_type: "client_credentials",
3477 client_id: client_id,
3478 client_secret: client_secret
3481 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3482 json_response(conn, 200)
3485 token_from_db = Repo.get_by(Token, token: token)
3486 assert token_from_db
3488 assert scope == "read write follow"
3492 |> put_req_header("authorization", "Bearer " <> token)
3493 |> post("/api/v1/accounts", %{
3495 email: "lain@example.org",
3496 password: "PlzDontHackLain",
3501 "access_token" => token,
3502 "created_at" => _created_at,
3504 "token_type" => "Bearer"
3505 } = json_response(conn, 200)
3507 token_from_db = Repo.get_by(Token, token: token)
3508 assert token_from_db
3509 token_from_db = Repo.preload(token_from_db, :user)
3510 assert token_from_db.user
3512 assert token_from_db.user.info.confirmation_pending
3515 test "rate limit", %{conn: conn} do
3516 app_token = insert(:oauth_token, user: nil)
3519 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3520 |> Map.put(:remote_ip, {15, 15, 15, 15})
3525 |> post("/api/v1/accounts", %{
3526 username: "#{i}lain",
3527 email: "#{i}lain@example.org",
3528 password: "PlzDontHackLain",
3533 "access_token" => token,
3534 "created_at" => _created_at,
3536 "token_type" => "Bearer"
3537 } = json_response(conn, 200)
3539 token_from_db = Repo.get_by(Token, token: token)
3540 assert token_from_db
3541 token_from_db = Repo.preload(token_from_db, :user)
3542 assert token_from_db.user
3544 assert token_from_db.user.info.confirmation_pending
3549 |> post("/api/v1/accounts", %{
3551 email: "6lain@example.org",
3552 password: "PlzDontHackLain",
3556 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3560 describe "GET /api/v1/polls/:id" do
3561 test "returns poll entity for object id", %{conn: conn} do
3562 user = insert(:user)
3565 CommonAPI.post(user, %{
3566 "status" => "Pleroma does",
3567 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3570 object = Object.normalize(activity)
3574 |> assign(:user, user)
3575 |> get("/api/v1/polls/#{object.id}")
3577 response = json_response(conn, 200)
3578 id = to_string(object.id)
3579 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3582 test "does not expose polls for private statuses", %{conn: conn} do
3583 user = insert(:user)
3584 other_user = insert(:user)
3587 CommonAPI.post(user, %{
3588 "status" => "Pleroma does",
3589 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3590 "visibility" => "private"
3593 object = Object.normalize(activity)
3597 |> assign(:user, other_user)
3598 |> get("/api/v1/polls/#{object.id}")
3600 assert json_response(conn, 404)
3604 describe "POST /api/v1/polls/:id/votes" do
3605 test "votes are added to the poll", %{conn: conn} do
3606 user = insert(:user)
3607 other_user = insert(:user)
3610 CommonAPI.post(user, %{
3611 "status" => "A very delicious sandwich",
3613 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3619 object = Object.normalize(activity)
3623 |> assign(:user, other_user)
3624 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3626 assert json_response(conn, 200)
3627 object = Object.get_by_id(object.id)
3629 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3634 test "author can't vote", %{conn: conn} do
3635 user = insert(:user)
3638 CommonAPI.post(user, %{
3639 "status" => "Am I cute?",
3640 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3643 object = Object.normalize(activity)
3646 |> assign(:user, user)
3647 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3648 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3650 object = Object.get_by_id(object.id)
3652 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3655 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3656 user = insert(:user)
3657 other_user = insert(:user)
3660 CommonAPI.post(user, %{
3661 "status" => "The glass is",
3662 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3665 object = Object.normalize(activity)
3668 |> assign(:user, other_user)
3669 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3670 |> json_response(422) == %{"error" => "Too many choices"}
3672 object = Object.get_by_id(object.id)
3674 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3679 test "does not allow choice index to be greater than options count", %{conn: conn} do
3680 user = insert(:user)
3681 other_user = insert(:user)
3684 CommonAPI.post(user, %{
3685 "status" => "Am I cute?",
3686 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3689 object = Object.normalize(activity)
3693 |> assign(:user, other_user)
3694 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3696 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3699 test "returns 404 error when object is not exist", %{conn: conn} do
3700 user = insert(:user)
3704 |> assign(:user, user)
3705 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3707 assert json_response(conn, 404) == %{"error" => "Record not found"}
3710 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3711 user = insert(:user)
3712 other_user = insert(:user)
3715 CommonAPI.post(user, %{
3716 "status" => "Am I cute?",
3717 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3718 "visibility" => "private"
3721 object = Object.normalize(activity)
3725 |> assign(:user, other_user)
3726 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3728 assert json_response(conn, 404) == %{"error" => "Record not found"}
3732 describe "GET /api/v1/statuses/:id/favourited_by" do
3734 user = insert(:user)
3735 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3739 |> assign(:user, user)
3741 [conn: conn, activity: activity]
3744 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3745 other_user = insert(:user)
3746 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3750 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3751 |> json_response(:ok)
3753 [%{"id" => id}] = response
3755 assert id == other_user.id
3758 test "returns empty array when status has not been favorited yet", %{
3764 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3765 |> json_response(:ok)
3767 assert Enum.empty?(response)
3770 test "does not return users who have favorited the status but are blocked", %{
3771 conn: %{assigns: %{user: user}} = conn,
3774 other_user = insert(:user)
3775 {:ok, user} = User.block(user, other_user)
3777 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3781 |> assign(:user, user)
3782 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3783 |> json_response(:ok)
3785 assert Enum.empty?(response)
3788 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3789 other_user = insert(:user)
3790 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3794 |> assign(:user, nil)
3795 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3796 |> json_response(:ok)
3798 [%{"id" => id}] = response
3799 assert id == other_user.id
3803 describe "GET /api/v1/statuses/:id/reblogged_by" do
3805 user = insert(:user)
3806 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3810 |> assign(:user, user)
3812 [conn: conn, activity: activity]
3815 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3816 other_user = insert(:user)
3817 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3821 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3822 |> json_response(:ok)
3824 [%{"id" => id}] = response
3826 assert id == other_user.id
3829 test "returns empty array when status has not been reblogged yet", %{
3835 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3836 |> json_response(:ok)
3838 assert Enum.empty?(response)
3841 test "does not return users who have reblogged the status but are blocked", %{
3842 conn: %{assigns: %{user: user}} = conn,
3845 other_user = insert(:user)
3846 {:ok, user} = User.block(user, other_user)
3848 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3852 |> assign(:user, user)
3853 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3854 |> json_response(:ok)
3856 assert Enum.empty?(response)
3859 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3860 other_user = insert(:user)
3861 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3865 |> assign(:user, nil)
3866 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3867 |> json_response(:ok)
3869 [%{"id" => id}] = response
3870 assert id == other_user.id
3874 describe "POST /auth/password, with valid parameters" do
3875 setup %{conn: conn} do
3876 user = insert(:user)
3877 conn = post(conn, "/auth/password?email=#{user.email}")
3878 %{conn: conn, user: user}
3881 test "it returns 204", %{conn: conn} do
3882 assert json_response(conn, :no_content)
3885 test "it creates a PasswordResetToken record for user", %{user: user} do
3886 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3890 test "it sends an email to user", %{user: user} do
3891 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3893 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3894 notify_email = Config.get([:instance, :notify_email])
3895 instance_name = Config.get([:instance, :name])
3898 from: {instance_name, notify_email},
3899 to: {user.name, user.email},
3900 html_body: email.html_body
3905 describe "POST /auth/password, with invalid parameters" do
3907 user = insert(:user)
3911 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3912 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3913 assert conn.status == 404
3914 assert conn.resp_body == ""
3917 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3918 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3919 conn = post(conn, "/auth/password?email=#{user.email}")
3920 assert conn.status == 400
3921 assert conn.resp_body == ""
3925 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3927 user = insert(:user)
3928 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3932 |> Changeset.change()
3933 |> Changeset.put_embed(:info, info_change)
3936 assert user.info.confirmation_pending
3941 clear_config([:instance, :account_activation_required]) do
3942 Config.put([:instance, :account_activation_required], true)
3945 test "resend account confirmation email", %{conn: conn, user: user} do
3947 |> assign(:user, user)
3948 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3949 |> json_response(:no_content)
3951 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3952 notify_email = Config.get([:instance, :notify_email])
3953 instance_name = Config.get([:instance, :name])
3956 from: {instance_name, notify_email},
3957 to: {user.name, user.email},
3958 html_body: email.html_body
3963 describe "GET /api/v1/suggestions" do
3965 user = insert(:user)
3966 other_user = insert(:user)
3967 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3968 url500 = "http://test500?#{host}&#{user.nickname}"
3969 url200 = "http://test200?#{host}&#{user.nickname}"
3972 %{method: :get, url: ^url500} ->
3973 %Tesla.Env{status: 500, body: "bad request"}
3975 %{method: :get, url: ^url200} ->
3979 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3981 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3985 [user: user, other_user: other_user]
3988 clear_config(:suggestions)
3990 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3991 Config.put([:suggestions, :enabled], false)
3995 |> assign(:user, user)
3996 |> get("/api/v1/suggestions")
3997 |> json_response(200)
4002 test "returns error", %{conn: conn, user: user} do
4003 Config.put([:suggestions, :enabled], true)
4004 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4008 |> assign(:user, user)
4009 |> get("/api/v1/suggestions")
4010 |> json_response(500)
4012 assert res == "Something went wrong"
4015 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4016 Config.put([:suggestions, :enabled], true)
4017 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4021 |> assign(:user, user)
4022 |> get("/api/v1/suggestions")
4023 |> json_response(200)
4028 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4029 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4033 "acct" => other_user.ap_id,
4034 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4035 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4036 "id" => other_user.id