1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.ActivityExpiration
12 alias Pleroma.Notification
15 alias Pleroma.ScheduledActivity
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.MastodonAPI.FilterView
20 alias Pleroma.Web.OAuth.App
21 alias Pleroma.Web.OAuth.Token
22 alias Pleroma.Web.OStatus
23 alias Pleroma.Web.Push
24 import Pleroma.Factory
25 import ExUnit.CaptureLog
27 import Swoosh.TestAssertions
29 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
32 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
36 clear_config([:instance, :public])
37 clear_config([:rich_media, :enabled])
39 test "the home timeline", %{conn: conn} do
41 following = insert(:user)
43 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
47 |> assign(:user, user)
48 |> get("/api/v1/timelines/home")
50 assert Enum.empty?(json_response(conn, 200))
52 {:ok, user} = User.follow(user, following)
56 |> assign(:user, user)
57 |> get("/api/v1/timelines/home")
59 assert [%{"content" => "test"}] = json_response(conn, 200)
62 test "the public timeline", %{conn: conn} do
63 following = insert(:user)
66 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
69 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
73 |> get("/api/v1/timelines/public", %{"local" => "False"})
75 assert length(json_response(conn, 200)) == 2
79 |> get("/api/v1/timelines/public", %{"local" => "True"})
81 assert [%{"content" => "test"}] = json_response(conn, 200)
85 |> get("/api/v1/timelines/public", %{"local" => "1"})
87 assert [%{"content" => "test"}] = json_response(conn, 200)
91 test "the public timeline when public is set to false", %{conn: conn} do
92 Config.put([:instance, :public], false)
95 |> get("/api/v1/timelines/public", %{"local" => "False"})
96 |> json_response(403) == %{"error" => "This resource requires authentication."}
99 test "the public timeline includes only public statuses for an authenticated user" do
104 |> assign(:user, user)
106 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test"})
107 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "private"})
108 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "unlisted"})
109 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
111 res_conn = get(conn, "/api/v1/timelines/public")
112 assert length(json_response(res_conn, 200)) == 1
115 describe "posting statuses" do
121 |> assign(:user, user)
126 test "posting a status", %{conn: conn} do
127 idempotency_key = "Pikachu rocks!"
131 |> put_req_header("idempotency-key", idempotency_key)
132 |> post("/api/v1/statuses", %{
134 "spoiler_text" => "2hu",
135 "sensitive" => "false"
138 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
140 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
142 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
143 json_response(conn_one, 200)
145 assert Activity.get_by_id(id)
149 |> put_req_header("idempotency-key", idempotency_key)
150 |> post("/api/v1/statuses", %{
152 "spoiler_text" => "2hu",
153 "sensitive" => "false"
156 assert %{"id" => second_id} = json_response(conn_two, 200)
157 assert id == second_id
161 |> post("/api/v1/statuses", %{
163 "spoiler_text" => "2hu",
164 "sensitive" => "false"
167 assert %{"id" => third_id} = json_response(conn_three, 200)
168 refute id == third_id
170 # An activity that will expire:
172 expires_in = 120 * 60
176 |> post("api/v1/statuses", %{
177 "status" => "oolong",
178 "expires_in" => expires_in
181 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
182 assert activity = Activity.get_by_id(fourth_id)
183 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
185 estimated_expires_at =
186 NaiveDateTime.utc_now()
187 |> NaiveDateTime.add(expires_in)
188 |> NaiveDateTime.truncate(:second)
190 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
191 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
193 assert fourth_response["pleroma"]["expires_at"] ==
194 NaiveDateTime.to_iso8601(expiration.scheduled_at)
197 test "replying to a status", %{conn: conn} do
199 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
203 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
205 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
207 activity = Activity.get_by_id(id)
209 assert activity.data["context"] == replied_to.data["context"]
210 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
213 test "replying to a direct message with visibility other than direct", %{conn: conn} do
215 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
217 Enum.each(["public", "private", "unlisted"], fn visibility ->
220 |> post("/api/v1/statuses", %{
221 "status" => "@#{user.nickname} hey",
222 "in_reply_to_id" => replied_to.id,
223 "visibility" => visibility
226 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
230 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
233 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
235 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
236 assert Activity.get_by_id(id)
239 test "posting a sensitive status", %{conn: conn} do
242 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
244 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
245 assert Activity.get_by_id(id)
248 test "posting a fake status", %{conn: conn} do
251 |> post("/api/v1/statuses", %{
253 "\"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"
256 real_status = json_response(real_conn, 200)
259 assert Object.get_by_ap_id(real_status["uri"])
263 |> Map.put("id", nil)
264 |> Map.put("url", nil)
265 |> Map.put("uri", nil)
266 |> Map.put("created_at", nil)
267 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
271 |> post("/api/v1/statuses", %{
273 "\"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",
277 fake_status = json_response(fake_conn, 200)
280 refute Object.get_by_ap_id(fake_status["uri"])
284 |> Map.put("id", nil)
285 |> Map.put("url", nil)
286 |> Map.put("uri", nil)
287 |> Map.put("created_at", nil)
288 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
290 assert real_status == fake_status
293 test "posting a status with OGP link preview", %{conn: conn} do
294 Config.put([:rich_media, :enabled], true)
298 |> post("/api/v1/statuses", %{
299 "status" => "https://example.com/ogp"
302 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
303 assert Activity.get_by_id(id)
306 test "posting a direct status", %{conn: conn} do
307 user2 = insert(:user)
308 content = "direct cofe @#{user2.nickname}"
312 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
314 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
315 assert activity = Activity.get_by_id(id)
316 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
317 assert activity.data["to"] == [user2.ap_id]
318 assert activity.data["cc"] == []
322 describe "posting polls" do
323 test "posting a poll", %{conn: conn} do
325 time = NaiveDateTime.utc_now()
329 |> assign(:user, user)
330 |> post("/api/v1/statuses", %{
331 "status" => "Who is the #bestgrill?",
332 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
335 response = json_response(conn, 200)
337 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
338 title in ["Rei", "Asuka", "Misato"]
341 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
342 refute response["poll"]["expred"]
345 test "option limit is enforced", %{conn: conn} do
347 limit = Config.get([:instance, :poll_limits, :max_options])
351 |> assign(:user, user)
352 |> post("/api/v1/statuses", %{
354 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
357 %{"error" => error} = json_response(conn, 422)
358 assert error == "Poll can't contain more than #{limit} options"
361 test "option character limit is enforced", %{conn: conn} do
363 limit = Config.get([:instance, :poll_limits, :max_option_chars])
367 |> assign(:user, user)
368 |> post("/api/v1/statuses", %{
371 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
376 %{"error" => error} = json_response(conn, 422)
377 assert error == "Poll options cannot be longer than #{limit} characters each"
380 test "minimal date limit is enforced", %{conn: conn} do
382 limit = Config.get([:instance, :poll_limits, :min_expiration])
386 |> assign(:user, user)
387 |> post("/api/v1/statuses", %{
388 "status" => "imagine arbitrary limits",
390 "options" => ["this post was made by pleroma gang"],
391 "expires_in" => limit - 1
395 %{"error" => error} = json_response(conn, 422)
396 assert error == "Expiration date is too soon"
399 test "maximum date limit is enforced", %{conn: conn} do
401 limit = Config.get([:instance, :poll_limits, :max_expiration])
405 |> assign(:user, user)
406 |> post("/api/v1/statuses", %{
407 "status" => "imagine arbitrary limits",
409 "options" => ["this post was made by pleroma gang"],
410 "expires_in" => limit + 1
414 %{"error" => error} = json_response(conn, 422)
415 assert error == "Expiration date is too far in the future"
419 test "direct timeline", %{conn: conn} do
420 user_one = insert(:user)
421 user_two = insert(:user)
423 {:ok, user_two} = User.follow(user_two, user_one)
426 CommonAPI.post(user_one, %{
427 "status" => "Hi @#{user_two.nickname}!",
428 "visibility" => "direct"
431 {:ok, _follower_only} =
432 CommonAPI.post(user_one, %{
433 "status" => "Hi @#{user_two.nickname}!",
434 "visibility" => "private"
437 # Only direct should be visible here
440 |> assign(:user, user_two)
441 |> get("api/v1/timelines/direct")
443 [status] = json_response(res_conn, 200)
445 assert %{"visibility" => "direct"} = status
446 assert status["url"] != direct.data["id"]
448 # User should be able to see their own direct message
451 |> assign(:user, user_one)
452 |> get("api/v1/timelines/direct")
454 [status] = json_response(res_conn, 200)
456 assert %{"visibility" => "direct"} = status
458 # Both should be visible here
461 |> assign(:user, user_two)
462 |> get("api/v1/timelines/home")
464 [_s1, _s2] = json_response(res_conn, 200)
467 Enum.each(1..20, fn _ ->
469 CommonAPI.post(user_one, %{
470 "status" => "Hi @#{user_two.nickname}!",
471 "visibility" => "direct"
477 |> assign(:user, user_two)
478 |> get("api/v1/timelines/direct")
480 statuses = json_response(res_conn, 200)
481 assert length(statuses) == 20
485 |> assign(:user, user_two)
486 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
488 [status] = json_response(res_conn, 200)
490 assert status["url"] != direct.data["id"]
493 test "Conversations", %{conn: conn} do
494 user_one = insert(:user)
495 user_two = insert(:user)
496 user_three = insert(:user)
498 {:ok, user_two} = User.follow(user_two, user_one)
501 CommonAPI.post(user_one, %{
502 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
503 "visibility" => "direct"
506 {:ok, _follower_only} =
507 CommonAPI.post(user_one, %{
508 "status" => "Hi @#{user_two.nickname}!",
509 "visibility" => "private"
514 |> assign(:user, user_one)
515 |> get("/api/v1/conversations")
517 assert response = json_response(res_conn, 200)
522 "accounts" => res_accounts,
523 "last_status" => res_last_status,
528 account_ids = Enum.map(res_accounts, & &1["id"])
529 assert length(res_accounts) == 2
530 assert user_two.id in account_ids
531 assert user_three.id in account_ids
532 assert is_binary(res_id)
533 assert unread == true
534 assert res_last_status["id"] == direct.id
536 # Apparently undocumented API endpoint
539 |> assign(:user, user_one)
540 |> post("/api/v1/conversations/#{res_id}/read")
542 assert response = json_response(res_conn, 200)
543 assert length(response["accounts"]) == 2
544 assert response["last_status"]["id"] == direct.id
545 assert response["unread"] == false
547 # (vanilla) Mastodon frontend behaviour
550 |> assign(:user, user_one)
551 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
553 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
556 test "doesn't include DMs from blocked users", %{conn: conn} do
557 blocker = insert(:user)
558 blocked = insert(:user)
560 {:ok, blocker} = User.block(blocker, blocked)
562 {:ok, _blocked_direct} =
563 CommonAPI.post(blocked, %{
564 "status" => "Hi @#{blocker.nickname}!",
565 "visibility" => "direct"
569 CommonAPI.post(user, %{
570 "status" => "Hi @#{blocker.nickname}!",
571 "visibility" => "direct"
576 |> assign(:user, user)
577 |> get("api/v1/timelines/direct")
579 [status] = json_response(res_conn, 200)
580 assert status["id"] == direct.id
583 test "verify_credentials", %{conn: conn} do
588 |> assign(:user, user)
589 |> get("/api/v1/accounts/verify_credentials")
591 response = json_response(conn, 200)
593 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
594 assert response["pleroma"]["chat_token"]
595 assert id == to_string(user.id)
598 test "verify_credentials default scope unlisted", %{conn: conn} do
599 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
603 |> assign(:user, user)
604 |> get("/api/v1/accounts/verify_credentials")
606 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
607 assert id == to_string(user.id)
610 test "apps/verify_credentials", %{conn: conn} do
611 token = insert(:oauth_token)
615 |> assign(:user, token.user)
616 |> assign(:token, token)
617 |> get("/api/v1/apps/verify_credentials")
619 app = Repo.preload(token, :app).app
622 "name" => app.client_name,
623 "website" => app.website,
624 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
627 assert expected == json_response(conn, 200)
630 test "user avatar can be set", %{conn: conn} do
632 avatar_image = File.read!("test/fixtures/avatar_data_uri")
636 |> assign(:user, user)
637 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
639 user = refresh_record(user)
653 assert %{"url" => _} = json_response(conn, 200)
656 test "user avatar can be reset", %{conn: conn} do
661 |> assign(:user, user)
662 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
664 user = User.get_cached_by_id(user.id)
666 assert user.avatar == nil
668 assert %{"url" => nil} = json_response(conn, 200)
671 test "can set profile banner", %{conn: conn} do
676 |> assign(:user, user)
677 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
679 user = refresh_record(user)
680 assert user.info.banner["type"] == "Image"
682 assert %{"url" => _} = json_response(conn, 200)
685 test "can reset profile banner", %{conn: conn} do
690 |> assign(:user, user)
691 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
693 user = refresh_record(user)
694 assert user.info.banner == %{}
696 assert %{"url" => nil} = json_response(conn, 200)
699 test "background image can be set", %{conn: conn} do
704 |> assign(:user, user)
705 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
707 user = refresh_record(user)
708 assert user.info.background["type"] == "Image"
709 assert %{"url" => _} = json_response(conn, 200)
712 test "background image can be reset", %{conn: conn} do
717 |> assign(:user, user)
718 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
720 user = refresh_record(user)
721 assert user.info.background == %{}
722 assert %{"url" => nil} = json_response(conn, 200)
725 test "creates an oauth app", %{conn: conn} do
727 app_attrs = build(:oauth_app)
731 |> assign(:user, user)
732 |> post("/api/v1/apps", %{
733 client_name: app_attrs.client_name,
734 redirect_uris: app_attrs.redirect_uris
737 [app] = Repo.all(App)
740 "name" => app.client_name,
741 "website" => app.website,
742 "client_id" => app.client_id,
743 "client_secret" => app.client_secret,
744 "id" => app.id |> to_string(),
745 "redirect_uri" => app.redirect_uris,
746 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
749 assert expected == json_response(conn, 200)
752 test "get a status", %{conn: conn} do
753 activity = insert(:note_activity)
757 |> get("/api/v1/statuses/#{activity.id}")
759 assert %{"id" => id} = json_response(conn, 200)
760 assert id == to_string(activity.id)
763 test "get statuses by IDs", %{conn: conn} do
764 %{id: id1} = insert(:note_activity)
765 %{id: id2} = insert(:note_activity)
767 query_string = "ids[]=#{id1}&ids[]=#{id2}"
768 conn = get(conn, "/api/v1/statuses/?#{query_string}")
770 assert [%{"id" => ^id1}, %{"id" => ^id2}] = json_response(conn, :ok)
773 describe "deleting a status" do
774 test "when you created it", %{conn: conn} do
775 activity = insert(:note_activity)
776 author = User.get_cached_by_ap_id(activity.data["actor"])
780 |> assign(:user, author)
781 |> delete("/api/v1/statuses/#{activity.id}")
783 assert %{} = json_response(conn, 200)
785 refute Activity.get_by_id(activity.id)
788 test "when you didn't create it", %{conn: conn} do
789 activity = insert(:note_activity)
794 |> assign(:user, user)
795 |> delete("/api/v1/statuses/#{activity.id}")
797 assert %{"error" => _} = json_response(conn, 403)
799 assert Activity.get_by_id(activity.id) == activity
802 test "when you're an admin or moderator", %{conn: conn} do
803 activity1 = insert(:note_activity)
804 activity2 = insert(:note_activity)
805 admin = insert(:user, info: %{is_admin: true})
806 moderator = insert(:user, info: %{is_moderator: true})
810 |> assign(:user, admin)
811 |> delete("/api/v1/statuses/#{activity1.id}")
813 assert %{} = json_response(res_conn, 200)
817 |> assign(:user, moderator)
818 |> delete("/api/v1/statuses/#{activity2.id}")
820 assert %{} = json_response(res_conn, 200)
822 refute Activity.get_by_id(activity1.id)
823 refute Activity.get_by_id(activity2.id)
827 describe "filters" do
828 test "creating a filter", %{conn: conn} do
831 filter = %Pleroma.Filter{
838 |> assign(:user, user)
839 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
841 assert response = json_response(conn, 200)
842 assert response["phrase"] == filter.phrase
843 assert response["context"] == filter.context
844 assert response["irreversible"] == false
845 assert response["id"] != nil
846 assert response["id"] != ""
849 test "fetching a list of filters", %{conn: conn} do
852 query_one = %Pleroma.Filter{
859 query_two = %Pleroma.Filter{
866 {:ok, filter_one} = Pleroma.Filter.create(query_one)
867 {:ok, filter_two} = Pleroma.Filter.create(query_two)
871 |> assign(:user, user)
872 |> get("/api/v1/filters")
873 |> json_response(200)
879 filters: [filter_two, filter_one]
883 test "get a filter", %{conn: conn} do
886 query = %Pleroma.Filter{
893 {:ok, filter} = Pleroma.Filter.create(query)
897 |> assign(:user, user)
898 |> get("/api/v1/filters/#{filter.filter_id}")
900 assert _response = json_response(conn, 200)
903 test "update a filter", %{conn: conn} do
906 query = %Pleroma.Filter{
913 {:ok, _filter} = Pleroma.Filter.create(query)
915 new = %Pleroma.Filter{
922 |> assign(:user, user)
923 |> put("/api/v1/filters/#{query.filter_id}", %{
928 assert response = json_response(conn, 200)
929 assert response["phrase"] == new.phrase
930 assert response["context"] == new.context
933 test "delete a filter", %{conn: conn} do
936 query = %Pleroma.Filter{
943 {:ok, filter} = Pleroma.Filter.create(query)
947 |> assign(:user, user)
948 |> delete("/api/v1/filters/#{filter.filter_id}")
950 assert response = json_response(conn, 200)
951 assert response == %{}
955 describe "list timelines" do
956 test "list timeline", %{conn: conn} do
958 other_user = insert(:user)
959 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
960 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
961 {:ok, list} = Pleroma.List.create("name", user)
962 {:ok, list} = Pleroma.List.follow(list, other_user)
966 |> assign(:user, user)
967 |> get("/api/v1/timelines/list/#{list.id}")
969 assert [%{"id" => id}] = json_response(conn, 200)
971 assert id == to_string(activity_two.id)
974 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
976 other_user = insert(:user)
977 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
979 {:ok, _activity_two} =
980 CommonAPI.post(other_user, %{
981 "status" => "Marisa is cute.",
982 "visibility" => "private"
985 {:ok, list} = Pleroma.List.create("name", user)
986 {:ok, list} = Pleroma.List.follow(list, other_user)
990 |> assign(:user, user)
991 |> get("/api/v1/timelines/list/#{list.id}")
993 assert [%{"id" => id}] = json_response(conn, 200)
995 assert id == to_string(activity_one.id)
999 describe "notifications" do
1000 test "list of notifications", %{conn: conn} do
1001 user = insert(:user)
1002 other_user = insert(:user)
1004 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1006 {:ok, [_notification]} = Notification.create_notifications(activity)
1010 |> assign(:user, user)
1011 |> get("/api/v1/notifications")
1014 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1016 }\">@<span>#{user.nickname}</span></a></span>"
1018 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1019 assert response == expected_response
1022 test "getting a single notification", %{conn: conn} do
1023 user = insert(:user)
1024 other_user = insert(:user)
1026 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1028 {:ok, [notification]} = Notification.create_notifications(activity)
1032 |> assign(:user, user)
1033 |> get("/api/v1/notifications/#{notification.id}")
1036 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1038 }\">@<span>#{user.nickname}</span></a></span>"
1040 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1041 assert response == expected_response
1044 test "dismissing a single notification", %{conn: conn} do
1045 user = insert(:user)
1046 other_user = insert(:user)
1048 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1050 {:ok, [notification]} = Notification.create_notifications(activity)
1054 |> assign(:user, user)
1055 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1057 assert %{} = json_response(conn, 200)
1060 test "clearing all notifications", %{conn: conn} do
1061 user = insert(:user)
1062 other_user = insert(:user)
1064 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1066 {:ok, [_notification]} = Notification.create_notifications(activity)
1070 |> assign(:user, user)
1071 |> post("/api/v1/notifications/clear")
1073 assert %{} = json_response(conn, 200)
1077 |> assign(:user, user)
1078 |> get("/api/v1/notifications")
1080 assert all = json_response(conn, 200)
1084 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1085 user = insert(:user)
1086 other_user = insert(:user)
1088 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1089 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1090 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1091 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1093 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1094 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1095 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1096 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1100 |> assign(:user, user)
1105 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1107 result = json_response(conn_res, 200)
1108 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1113 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1115 result = json_response(conn_res, 200)
1116 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1121 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1123 result = json_response(conn_res, 200)
1124 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1127 test "filters notifications using exclude_types", %{conn: conn} do
1128 user = insert(:user)
1129 other_user = insert(:user)
1131 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1132 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1133 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1134 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1135 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1137 mention_notification_id =
1138 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1140 favorite_notification_id =
1141 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1143 reblog_notification_id =
1144 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1146 follow_notification_id =
1147 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1151 |> assign(:user, user)
1154 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1156 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1159 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1161 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1164 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1166 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1169 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1171 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1174 test "destroy multiple", %{conn: conn} do
1175 user = insert(:user)
1176 other_user = insert(:user)
1178 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1179 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1180 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1181 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1183 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1184 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1185 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1186 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1190 |> assign(:user, user)
1194 |> get("/api/v1/notifications")
1196 result = json_response(conn_res, 200)
1197 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1201 |> assign(:user, other_user)
1205 |> get("/api/v1/notifications")
1207 result = json_response(conn_res, 200)
1208 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1212 |> delete("/api/v1/notifications/destroy_multiple", %{
1213 "ids" => [notification1_id, notification2_id]
1216 assert json_response(conn_destroy, 200) == %{}
1220 |> get("/api/v1/notifications")
1222 result = json_response(conn_res, 200)
1223 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1226 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1227 user = insert(:user)
1228 user2 = insert(:user)
1230 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1231 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1233 conn = assign(conn, :user, user)
1235 conn = get(conn, "/api/v1/notifications")
1237 assert length(json_response(conn, 200)) == 1
1239 {:ok, user} = User.mute(user, user2)
1241 conn = assign(build_conn(), :user, user)
1242 conn = get(conn, "/api/v1/notifications")
1244 assert json_response(conn, 200) == []
1247 test "see notifications after muting user without notifications", %{conn: conn} do
1248 user = insert(:user)
1249 user2 = insert(:user)
1251 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1252 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1254 conn = assign(conn, :user, user)
1256 conn = get(conn, "/api/v1/notifications")
1258 assert length(json_response(conn, 200)) == 1
1260 {:ok, user} = User.mute(user, user2, false)
1262 conn = assign(build_conn(), :user, user)
1263 conn = get(conn, "/api/v1/notifications")
1265 assert length(json_response(conn, 200)) == 1
1268 test "see notifications after muting user with notifications and with_muted parameter", %{
1271 user = insert(:user)
1272 user2 = insert(:user)
1274 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1275 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1277 conn = assign(conn, :user, user)
1279 conn = get(conn, "/api/v1/notifications")
1281 assert length(json_response(conn, 200)) == 1
1283 {:ok, user} = User.mute(user, user2)
1285 conn = assign(build_conn(), :user, user)
1286 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1288 assert length(json_response(conn, 200)) == 1
1292 describe "reblogging" do
1293 test "reblogs and returns the reblogged status", %{conn: conn} do
1294 activity = insert(:note_activity)
1295 user = insert(:user)
1299 |> assign(:user, user)
1300 |> post("/api/v1/statuses/#{activity.id}/reblog")
1303 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1305 } = json_response(conn, 200)
1307 assert to_string(activity.id) == id
1310 test "reblogged status for another user", %{conn: conn} do
1311 activity = insert(:note_activity)
1312 user1 = insert(:user)
1313 user2 = insert(:user)
1314 user3 = insert(:user)
1315 CommonAPI.favorite(activity.id, user2)
1316 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1317 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1318 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1322 |> assign(:user, user3)
1323 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1326 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1327 "reblogged" => false,
1328 "favourited" => false,
1329 "bookmarked" => false
1330 } = json_response(conn_res, 200)
1334 |> assign(:user, user2)
1335 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1338 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1339 "reblogged" => true,
1340 "favourited" => true,
1341 "bookmarked" => true
1342 } = json_response(conn_res, 200)
1344 assert to_string(activity.id) == id
1347 test "returns 400 error when activity is not exist", %{conn: conn} do
1348 user = insert(:user)
1352 |> assign(:user, user)
1353 |> post("/api/v1/statuses/foo/reblog")
1355 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1359 describe "unreblogging" do
1360 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1361 activity = insert(:note_activity)
1362 user = insert(:user)
1364 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1368 |> assign(:user, user)
1369 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1371 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1373 assert to_string(activity.id) == id
1376 test "returns 400 error when activity is not exist", %{conn: conn} do
1377 user = insert(:user)
1381 |> assign(:user, user)
1382 |> post("/api/v1/statuses/foo/unreblog")
1384 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1388 describe "favoriting" do
1389 test "favs a status and returns it", %{conn: conn} do
1390 activity = insert(:note_activity)
1391 user = insert(:user)
1395 |> assign(:user, user)
1396 |> post("/api/v1/statuses/#{activity.id}/favourite")
1398 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1399 json_response(conn, 200)
1401 assert to_string(activity.id) == id
1404 test "returns 400 error for a wrong id", %{conn: conn} do
1405 user = insert(:user)
1409 |> assign(:user, user)
1410 |> post("/api/v1/statuses/1/favourite")
1412 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1416 describe "unfavoriting" do
1417 test "unfavorites a status and returns it", %{conn: conn} do
1418 activity = insert(:note_activity)
1419 user = insert(:user)
1421 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1425 |> assign(:user, user)
1426 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1428 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1429 json_response(conn, 200)
1431 assert to_string(activity.id) == id
1434 test "returns 400 error for a wrong id", %{conn: conn} do
1435 user = insert(:user)
1439 |> assign(:user, user)
1440 |> post("/api/v1/statuses/1/unfavourite")
1442 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1446 describe "user timelines" do
1447 test "gets a users statuses", %{conn: conn} do
1448 user_one = insert(:user)
1449 user_two = insert(:user)
1450 user_three = insert(:user)
1452 {:ok, user_three} = User.follow(user_three, user_one)
1454 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1456 {:ok, direct_activity} =
1457 CommonAPI.post(user_one, %{
1458 "status" => "Hi, @#{user_two.nickname}.",
1459 "visibility" => "direct"
1462 {:ok, private_activity} =
1463 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1467 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1469 assert [%{"id" => id}] = json_response(resp, 200)
1470 assert id == to_string(activity.id)
1474 |> assign(:user, user_two)
1475 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1477 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1478 assert id_one == to_string(direct_activity.id)
1479 assert id_two == to_string(activity.id)
1483 |> assign(:user, user_three)
1484 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1486 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1487 assert id_one == to_string(private_activity.id)
1488 assert id_two == to_string(activity.id)
1491 test "unimplemented pinned statuses feature", %{conn: conn} do
1492 note = insert(:note_activity)
1493 user = User.get_cached_by_ap_id(note.data["actor"])
1497 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1499 assert json_response(conn, 200) == []
1502 test "gets an users media", %{conn: conn} do
1503 note = insert(:note_activity)
1504 user = User.get_cached_by_ap_id(note.data["actor"])
1506 file = %Plug.Upload{
1507 content_type: "image/jpg",
1508 path: Path.absname("test/fixtures/image.jpg"),
1509 filename: "an_image.jpg"
1512 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1514 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1518 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1520 assert [%{"id" => id}] = json_response(conn, 200)
1521 assert id == to_string(image_post.id)
1525 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1527 assert [%{"id" => id}] = json_response(conn, 200)
1528 assert id == to_string(image_post.id)
1531 test "gets a user's statuses without reblogs", %{conn: conn} do
1532 user = insert(:user)
1533 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1534 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1538 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1540 assert [%{"id" => id}] = json_response(conn, 200)
1541 assert id == to_string(post.id)
1545 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1547 assert [%{"id" => id}] = json_response(conn, 200)
1548 assert id == to_string(post.id)
1551 test "filters user's statuses by a hashtag", %{conn: conn} do
1552 user = insert(:user)
1553 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1554 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1558 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1560 assert [%{"id" => id}] = json_response(conn, 200)
1561 assert id == to_string(post.id)
1565 describe "user relationships" do
1566 test "returns the relationships for the current user", %{conn: conn} do
1567 user = insert(:user)
1568 other_user = insert(:user)
1569 {:ok, user} = User.follow(user, other_user)
1573 |> assign(:user, user)
1574 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1576 assert [relationship] = json_response(conn, 200)
1578 assert to_string(other_user.id) == relationship["id"]
1582 describe "media upload" do
1584 user = insert(:user)
1588 |> assign(:user, user)
1590 image = %Plug.Upload{
1591 content_type: "image/jpg",
1592 path: Path.absname("test/fixtures/image.jpg"),
1593 filename: "an_image.jpg"
1596 [conn: conn, image: image]
1599 clear_config([:media_proxy])
1600 clear_config([Pleroma.Upload])
1602 test "returns uploaded image", %{conn: conn, image: image} do
1603 desc = "Description of the image"
1607 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1608 |> json_response(:ok)
1610 assert media["type"] == "image"
1611 assert media["description"] == desc
1614 object = Repo.get(Object, media["id"])
1615 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1619 describe "locked accounts" do
1620 test "/api/v1/follow_requests works" do
1621 user = insert(:user, %{info: %User.Info{locked: true}})
1622 other_user = insert(:user)
1624 {:ok, _activity} = ActivityPub.follow(other_user, user)
1626 user = User.get_cached_by_id(user.id)
1627 other_user = User.get_cached_by_id(other_user.id)
1629 assert User.following?(other_user, user) == false
1633 |> assign(:user, user)
1634 |> get("/api/v1/follow_requests")
1636 assert [relationship] = json_response(conn, 200)
1637 assert to_string(other_user.id) == relationship["id"]
1640 test "/api/v1/follow_requests/:id/authorize works" do
1641 user = insert(:user, %{info: %User.Info{locked: true}})
1642 other_user = insert(:user)
1644 {:ok, _activity} = ActivityPub.follow(other_user, user)
1646 user = User.get_cached_by_id(user.id)
1647 other_user = User.get_cached_by_id(other_user.id)
1649 assert User.following?(other_user, user) == false
1653 |> assign(:user, user)
1654 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1656 assert relationship = json_response(conn, 200)
1657 assert to_string(other_user.id) == relationship["id"]
1659 user = User.get_cached_by_id(user.id)
1660 other_user = User.get_cached_by_id(other_user.id)
1662 assert User.following?(other_user, user) == true
1665 test "verify_credentials", %{conn: conn} do
1666 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1670 |> assign(:user, user)
1671 |> get("/api/v1/accounts/verify_credentials")
1673 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1674 assert id == to_string(user.id)
1677 test "/api/v1/follow_requests/:id/reject works" do
1678 user = insert(:user, %{info: %User.Info{locked: true}})
1679 other_user = insert(:user)
1681 {:ok, _activity} = ActivityPub.follow(other_user, user)
1683 user = User.get_cached_by_id(user.id)
1687 |> assign(:user, user)
1688 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1690 assert relationship = json_response(conn, 200)
1691 assert to_string(other_user.id) == relationship["id"]
1693 user = User.get_cached_by_id(user.id)
1694 other_user = User.get_cached_by_id(other_user.id)
1696 assert User.following?(other_user, user) == false
1700 describe "account fetching" do
1701 test "works by id" do
1702 user = insert(:user)
1706 |> get("/api/v1/accounts/#{user.id}")
1708 assert %{"id" => id} = json_response(conn, 200)
1709 assert id == to_string(user.id)
1713 |> get("/api/v1/accounts/-1")
1715 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1718 test "works by nickname" do
1719 user = insert(:user)
1723 |> get("/api/v1/accounts/#{user.nickname}")
1725 assert %{"id" => id} = json_response(conn, 200)
1726 assert id == user.id
1729 test "works by nickname for remote users" do
1730 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1731 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1732 user = insert(:user, nickname: "user@example.com", local: false)
1736 |> get("/api/v1/accounts/#{user.nickname}")
1738 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1739 assert %{"id" => id} = json_response(conn, 200)
1740 assert id == user.id
1743 test "respects limit_to_local_content == :all for remote user nicknames" do
1744 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1745 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1747 user = insert(:user, nickname: "user@example.com", local: false)
1751 |> get("/api/v1/accounts/#{user.nickname}")
1753 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1754 assert json_response(conn, 404)
1757 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1758 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1759 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1761 user = insert(:user, nickname: "user@example.com", local: false)
1762 reading_user = insert(:user)
1766 |> get("/api/v1/accounts/#{user.nickname}")
1768 assert json_response(conn, 404)
1772 |> assign(:user, reading_user)
1773 |> get("/api/v1/accounts/#{user.nickname}")
1775 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1776 assert %{"id" => id} = json_response(conn, 200)
1777 assert id == user.id
1781 test "mascot upload", %{conn: conn} do
1782 user = insert(:user)
1784 non_image_file = %Plug.Upload{
1785 content_type: "audio/mpeg",
1786 path: Path.absname("test/fixtures/sound.mp3"),
1787 filename: "sound.mp3"
1792 |> assign(:user, user)
1793 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1795 assert json_response(conn, 415)
1797 file = %Plug.Upload{
1798 content_type: "image/jpg",
1799 path: Path.absname("test/fixtures/image.jpg"),
1800 filename: "an_image.jpg"
1805 |> assign(:user, user)
1806 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1808 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1811 test "mascot retrieving", %{conn: conn} do
1812 user = insert(:user)
1813 # When user hasn't set a mascot, we should just get pleroma tan back
1816 |> assign(:user, user)
1817 |> get("/api/v1/pleroma/mascot")
1819 assert %{"url" => url} = json_response(conn, 200)
1820 assert url =~ "pleroma-fox-tan-smol"
1822 # When a user sets their mascot, we should get that back
1823 file = %Plug.Upload{
1824 content_type: "image/jpg",
1825 path: Path.absname("test/fixtures/image.jpg"),
1826 filename: "an_image.jpg"
1831 |> assign(:user, user)
1832 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1834 assert json_response(conn, 200)
1836 user = User.get_cached_by_id(user.id)
1840 |> assign(:user, user)
1841 |> get("/api/v1/pleroma/mascot")
1843 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1844 assert url =~ "an_image"
1847 test "hashtag timeline", %{conn: conn} do
1848 following = insert(:user)
1851 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1853 {:ok, [_activity]} =
1854 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1858 |> get("/api/v1/timelines/tag/2hu")
1860 assert [%{"id" => id}] = json_response(nconn, 200)
1862 assert id == to_string(activity.id)
1864 # works for different capitalization too
1867 |> get("/api/v1/timelines/tag/2HU")
1869 assert [%{"id" => id}] = json_response(nconn, 200)
1871 assert id == to_string(activity.id)
1875 test "multi-hashtag timeline", %{conn: conn} do
1876 user = insert(:user)
1878 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1879 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1880 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1884 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1886 [status_none, status_test1, status_test] = json_response(any_test, 200)
1888 assert to_string(activity_test.id) == status_test["id"]
1889 assert to_string(activity_test1.id) == status_test1["id"]
1890 assert to_string(activity_none.id) == status_none["id"]
1894 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1896 assert [status_test1] == json_response(restricted_test, 200)
1898 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1900 assert [status_none] == json_response(all_test, 200)
1903 test "getting followers", %{conn: conn} do
1904 user = insert(:user)
1905 other_user = insert(:user)
1906 {:ok, user} = User.follow(user, other_user)
1910 |> get("/api/v1/accounts/#{other_user.id}/followers")
1912 assert [%{"id" => id}] = json_response(conn, 200)
1913 assert id == to_string(user.id)
1916 test "getting followers, hide_followers", %{conn: conn} do
1917 user = insert(:user)
1918 other_user = insert(:user, %{info: %{hide_followers: true}})
1919 {:ok, _user} = User.follow(user, other_user)
1923 |> get("/api/v1/accounts/#{other_user.id}/followers")
1925 assert [] == json_response(conn, 200)
1928 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1929 user = insert(:user)
1930 other_user = insert(:user, %{info: %{hide_followers: true}})
1931 {:ok, _user} = User.follow(user, other_user)
1935 |> assign(:user, other_user)
1936 |> get("/api/v1/accounts/#{other_user.id}/followers")
1938 refute [] == json_response(conn, 200)
1941 test "getting followers, pagination", %{conn: conn} do
1942 user = insert(:user)
1943 follower1 = insert(:user)
1944 follower2 = insert(:user)
1945 follower3 = insert(:user)
1946 {:ok, _} = User.follow(follower1, user)
1947 {:ok, _} = User.follow(follower2, user)
1948 {:ok, _} = User.follow(follower3, user)
1952 |> assign(:user, user)
1956 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1958 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1959 assert id3 == follower3.id
1960 assert id2 == follower2.id
1964 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1966 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1967 assert id2 == follower2.id
1968 assert id1 == follower1.id
1972 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1974 assert [%{"id" => id2}] = json_response(res_conn, 200)
1975 assert id2 == follower2.id
1977 assert [link_header] = get_resp_header(res_conn, "link")
1978 assert link_header =~ ~r/min_id=#{follower2.id}/
1979 assert link_header =~ ~r/max_id=#{follower2.id}/
1982 test "getting following", %{conn: conn} do
1983 user = insert(:user)
1984 other_user = insert(:user)
1985 {:ok, user} = User.follow(user, other_user)
1989 |> get("/api/v1/accounts/#{user.id}/following")
1991 assert [%{"id" => id}] = json_response(conn, 200)
1992 assert id == to_string(other_user.id)
1995 test "getting following, hide_follows", %{conn: conn} do
1996 user = insert(:user, %{info: %{hide_follows: true}})
1997 other_user = insert(:user)
1998 {:ok, user} = User.follow(user, other_user)
2002 |> get("/api/v1/accounts/#{user.id}/following")
2004 assert [] == json_response(conn, 200)
2007 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2008 user = insert(:user, %{info: %{hide_follows: true}})
2009 other_user = insert(:user)
2010 {:ok, user} = User.follow(user, other_user)
2014 |> assign(:user, user)
2015 |> get("/api/v1/accounts/#{user.id}/following")
2017 refute [] == json_response(conn, 200)
2020 test "getting following, pagination", %{conn: conn} do
2021 user = insert(:user)
2022 following1 = insert(:user)
2023 following2 = insert(:user)
2024 following3 = insert(:user)
2025 {:ok, _} = User.follow(user, following1)
2026 {:ok, _} = User.follow(user, following2)
2027 {:ok, _} = User.follow(user, following3)
2031 |> assign(:user, user)
2035 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2037 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2038 assert id3 == following3.id
2039 assert id2 == following2.id
2043 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2045 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2046 assert id2 == following2.id
2047 assert id1 == following1.id
2051 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2053 assert [%{"id" => id2}] = json_response(res_conn, 200)
2054 assert id2 == following2.id
2056 assert [link_header] = get_resp_header(res_conn, "link")
2057 assert link_header =~ ~r/min_id=#{following2.id}/
2058 assert link_header =~ ~r/max_id=#{following2.id}/
2061 test "following / unfollowing a user", %{conn: conn} do
2062 user = insert(:user)
2063 other_user = insert(:user)
2067 |> assign(:user, user)
2068 |> post("/api/v1/accounts/#{other_user.id}/follow")
2070 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2072 user = User.get_cached_by_id(user.id)
2076 |> assign(:user, user)
2077 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2079 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2081 user = User.get_cached_by_id(user.id)
2085 |> assign(:user, user)
2086 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2088 assert %{"id" => id} = json_response(conn, 200)
2089 assert id == to_string(other_user.id)
2092 test "following without reblogs" do
2093 follower = insert(:user)
2094 followed = insert(:user)
2095 other_user = insert(:user)
2099 |> assign(:user, follower)
2100 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2102 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2104 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2105 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2109 |> assign(:user, User.get_cached_by_id(follower.id))
2110 |> get("/api/v1/timelines/home")
2112 assert [] == json_response(conn, 200)
2116 |> assign(:user, follower)
2117 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2119 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2123 |> assign(:user, User.get_cached_by_id(follower.id))
2124 |> get("/api/v1/timelines/home")
2126 expected_activity_id = reblog.id
2127 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2130 test "following / unfollowing errors" do
2131 user = insert(:user)
2135 |> assign(:user, user)
2138 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2139 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2142 user = User.get_cached_by_id(user.id)
2143 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2144 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2146 # self follow via uri
2147 user = User.get_cached_by_id(user.id)
2148 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2149 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2151 # follow non existing user
2152 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2153 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2155 # follow non existing user via uri
2156 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2157 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2159 # unfollow non existing user
2160 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2161 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2164 describe "mute/unmute" do
2165 test "with notifications", %{conn: conn} do
2166 user = insert(:user)
2167 other_user = insert(:user)
2171 |> assign(:user, user)
2172 |> post("/api/v1/accounts/#{other_user.id}/mute")
2174 response = json_response(conn, 200)
2176 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2177 user = User.get_cached_by_id(user.id)
2181 |> assign(:user, user)
2182 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2184 response = json_response(conn, 200)
2185 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2188 test "without notifications", %{conn: conn} do
2189 user = insert(:user)
2190 other_user = insert(:user)
2194 |> assign(:user, user)
2195 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2197 response = json_response(conn, 200)
2199 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2200 user = User.get_cached_by_id(user.id)
2204 |> assign(:user, user)
2205 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2207 response = json_response(conn, 200)
2208 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2212 test "subscribing / unsubscribing to a user", %{conn: conn} do
2213 user = insert(:user)
2214 subscription_target = insert(:user)
2218 |> assign(:user, user)
2219 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2221 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2225 |> assign(:user, user)
2226 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2228 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2231 test "getting a list of mutes", %{conn: conn} do
2232 user = insert(:user)
2233 other_user = insert(:user)
2235 {:ok, user} = User.mute(user, other_user)
2239 |> assign(:user, user)
2240 |> get("/api/v1/mutes")
2242 other_user_id = to_string(other_user.id)
2243 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2246 test "blocking / unblocking a user", %{conn: conn} do
2247 user = insert(:user)
2248 other_user = insert(:user)
2252 |> assign(:user, user)
2253 |> post("/api/v1/accounts/#{other_user.id}/block")
2255 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2257 user = User.get_cached_by_id(user.id)
2261 |> assign(:user, user)
2262 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2264 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2267 test "getting a list of blocks", %{conn: conn} do
2268 user = insert(:user)
2269 other_user = insert(:user)
2271 {:ok, user} = User.block(user, other_user)
2275 |> assign(:user, user)
2276 |> get("/api/v1/blocks")
2278 other_user_id = to_string(other_user.id)
2279 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2282 test "blocking / unblocking a domain", %{conn: conn} do
2283 user = insert(:user)
2284 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2288 |> assign(:user, user)
2289 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2291 assert %{} = json_response(conn, 200)
2292 user = User.get_cached_by_ap_id(user.ap_id)
2293 assert User.blocks?(user, other_user)
2297 |> assign(:user, user)
2298 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2300 assert %{} = json_response(conn, 200)
2301 user = User.get_cached_by_ap_id(user.ap_id)
2302 refute User.blocks?(user, other_user)
2305 test "getting a list of domain blocks", %{conn: conn} do
2306 user = insert(:user)
2308 {:ok, user} = User.block_domain(user, "bad.site")
2309 {:ok, user} = User.block_domain(user, "even.worse.site")
2313 |> assign(:user, user)
2314 |> get("/api/v1/domain_blocks")
2316 domain_blocks = json_response(conn, 200)
2318 assert "bad.site" in domain_blocks
2319 assert "even.worse.site" in domain_blocks
2322 test "unimplemented follow_requests, blocks, domain blocks" do
2323 user = insert(:user)
2325 ["blocks", "domain_blocks", "follow_requests"]
2326 |> Enum.each(fn endpoint ->
2329 |> assign(:user, user)
2330 |> get("/api/v1/#{endpoint}")
2332 assert [] = json_response(conn, 200)
2336 test "returns the favorites of a user", %{conn: conn} do
2337 user = insert(:user)
2338 other_user = insert(:user)
2340 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2341 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2343 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2347 |> assign(:user, user)
2348 |> get("/api/v1/favourites")
2350 assert [status] = json_response(first_conn, 200)
2351 assert status["id"] == to_string(activity.id)
2353 assert [{"link", _link_header}] =
2354 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2356 # Honours query params
2357 {:ok, second_activity} =
2358 CommonAPI.post(other_user, %{
2360 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2363 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2365 last_like = status["id"]
2369 |> assign(:user, user)
2370 |> get("/api/v1/favourites?since_id=#{last_like}")
2372 assert [second_status] = json_response(second_conn, 200)
2373 assert second_status["id"] == to_string(second_activity.id)
2377 |> assign(:user, user)
2378 |> get("/api/v1/favourites?limit=0")
2380 assert [] = json_response(third_conn, 200)
2383 describe "getting favorites timeline of specified user" do
2385 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2386 [current_user: current_user, user: user]
2389 test "returns list of statuses favorited by specified user", %{
2391 current_user: current_user,
2394 [activity | _] = insert_pair(:note_activity)
2395 CommonAPI.favorite(activity.id, user)
2399 |> assign(:user, current_user)
2400 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2401 |> json_response(:ok)
2405 assert length(response) == 1
2406 assert like["id"] == activity.id
2409 test "returns favorites for specified user_id when user is not logged in", %{
2413 activity = insert(:note_activity)
2414 CommonAPI.favorite(activity.id, user)
2418 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2419 |> json_response(:ok)
2421 assert length(response) == 1
2424 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2426 current_user: current_user,
2430 CommonAPI.post(current_user, %{
2431 "status" => "Hi @#{user.nickname}!",
2432 "visibility" => "direct"
2435 CommonAPI.favorite(direct.id, user)
2439 |> assign(:user, current_user)
2440 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2441 |> json_response(:ok)
2443 assert length(response) == 1
2445 anonymous_response =
2447 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2448 |> json_response(:ok)
2450 assert Enum.empty?(anonymous_response)
2453 test "does not return others' favorited DM when user is not one of recipients", %{
2455 current_user: current_user,
2458 user_two = insert(:user)
2461 CommonAPI.post(user_two, %{
2462 "status" => "Hi @#{user.nickname}!",
2463 "visibility" => "direct"
2466 CommonAPI.favorite(direct.id, user)
2470 |> assign(:user, current_user)
2471 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2472 |> json_response(:ok)
2474 assert Enum.empty?(response)
2477 test "paginates favorites using since_id and max_id", %{
2479 current_user: current_user,
2482 activities = insert_list(10, :note_activity)
2484 Enum.each(activities, fn activity ->
2485 CommonAPI.favorite(activity.id, user)
2488 third_activity = Enum.at(activities, 2)
2489 seventh_activity = Enum.at(activities, 6)
2493 |> assign(:user, current_user)
2494 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2495 since_id: third_activity.id,
2496 max_id: seventh_activity.id
2498 |> json_response(:ok)
2500 assert length(response) == 3
2501 refute third_activity in response
2502 refute seventh_activity in response
2505 test "limits favorites using limit parameter", %{
2507 current_user: current_user,
2511 |> insert_list(:note_activity)
2512 |> Enum.each(fn activity ->
2513 CommonAPI.favorite(activity.id, user)
2518 |> assign(:user, current_user)
2519 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2520 |> json_response(:ok)
2522 assert length(response) == 3
2525 test "returns empty response when user does not have any favorited statuses", %{
2527 current_user: current_user,
2532 |> assign(:user, current_user)
2533 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2534 |> json_response(:ok)
2536 assert Enum.empty?(response)
2539 test "returns 404 error when specified user is not exist", %{conn: conn} do
2540 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2542 assert json_response(conn, 404) == %{"error" => "Record not found"}
2545 test "returns 403 error when user has hidden own favorites", %{
2547 current_user: current_user
2549 user = insert(:user, %{info: %{hide_favorites: true}})
2550 activity = insert(:note_activity)
2551 CommonAPI.favorite(activity.id, user)
2555 |> assign(:user, current_user)
2556 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2558 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2561 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2562 user = insert(:user)
2563 activity = insert(:note_activity)
2564 CommonAPI.favorite(activity.id, user)
2568 |> assign(:user, current_user)
2569 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2571 assert user.info.hide_favorites
2572 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2576 test "get instance information", %{conn: conn} do
2577 conn = get(conn, "/api/v1/instance")
2578 assert result = json_response(conn, 200)
2580 email = Config.get([:instance, :email])
2581 # Note: not checking for "max_toot_chars" since it's optional
2587 "email" => from_config_email,
2589 "streaming_api" => _
2594 "registrations" => _,
2598 assert email == from_config_email
2601 test "get instance stats", %{conn: conn} do
2602 user = insert(:user, %{local: true})
2604 user2 = insert(:user, %{local: true})
2605 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2607 insert(:user, %{local: false, nickname: "u@peer1.com"})
2608 insert(:user, %{local: false, nickname: "u@peer2.com"})
2610 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2612 # Stats should count users with missing or nil `info.deactivated` value
2613 user = User.get_cached_by_id(user.id)
2614 info_change = Changeset.change(user.info, %{deactivated: nil})
2618 |> Changeset.change()
2619 |> Changeset.put_embed(:info, info_change)
2620 |> User.update_and_set_cache()
2622 Pleroma.Stats.force_update()
2624 conn = get(conn, "/api/v1/instance")
2626 assert result = json_response(conn, 200)
2628 stats = result["stats"]
2631 assert stats["user_count"] == 1
2632 assert stats["status_count"] == 1
2633 assert stats["domain_count"] == 2
2636 test "get peers", %{conn: conn} do
2637 insert(:user, %{local: false, nickname: "u@peer1.com"})
2638 insert(:user, %{local: false, nickname: "u@peer2.com"})
2640 Pleroma.Stats.force_update()
2642 conn = get(conn, "/api/v1/instance/peers")
2644 assert result = json_response(conn, 200)
2646 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2649 test "put settings", %{conn: conn} do
2650 user = insert(:user)
2654 |> assign(:user, user)
2655 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2657 assert _result = json_response(conn, 200)
2659 user = User.get_cached_by_ap_id(user.ap_id)
2660 assert user.info.settings == %{"programming" => "socks"}
2663 describe "pinned statuses" do
2665 user = insert(:user)
2666 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2668 [user: user, activity: activity]
2671 clear_config([:instance, :max_pinned_statuses]) do
2672 Config.put([:instance, :max_pinned_statuses], 1)
2675 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2676 {:ok, _} = CommonAPI.pin(activity.id, user)
2680 |> assign(:user, user)
2681 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2682 |> json_response(200)
2684 id_str = to_string(activity.id)
2686 assert [%{"id" => ^id_str, "pinned" => true}] = result
2689 test "pin status", %{conn: conn, user: user, activity: activity} do
2690 id_str = to_string(activity.id)
2692 assert %{"id" => ^id_str, "pinned" => true} =
2694 |> assign(:user, user)
2695 |> post("/api/v1/statuses/#{activity.id}/pin")
2696 |> json_response(200)
2698 assert [%{"id" => ^id_str, "pinned" => true}] =
2700 |> assign(:user, user)
2701 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2702 |> json_response(200)
2705 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2706 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2710 |> assign(:user, user)
2711 |> post("/api/v1/statuses/#{dm.id}/pin")
2713 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2716 test "unpin status", %{conn: conn, user: user, activity: activity} do
2717 {:ok, _} = CommonAPI.pin(activity.id, user)
2719 id_str = to_string(activity.id)
2720 user = refresh_record(user)
2722 assert %{"id" => ^id_str, "pinned" => false} =
2724 |> assign(:user, user)
2725 |> post("/api/v1/statuses/#{activity.id}/unpin")
2726 |> json_response(200)
2730 |> assign(:user, user)
2731 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2732 |> json_response(200)
2735 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2738 |> assign(:user, user)
2739 |> post("/api/v1/statuses/1/unpin")
2741 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2744 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2745 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2747 id_str_one = to_string(activity_one.id)
2749 assert %{"id" => ^id_str_one, "pinned" => true} =
2751 |> assign(:user, user)
2752 |> post("/api/v1/statuses/#{id_str_one}/pin")
2753 |> json_response(200)
2755 user = refresh_record(user)
2757 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2759 |> assign(:user, user)
2760 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2761 |> json_response(400)
2767 Config.put([:rich_media, :enabled], true)
2769 user = insert(:user)
2773 test "returns rich-media card", %{conn: conn, user: user} do
2774 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2777 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2778 "provider_name" => "example.com",
2779 "provider_url" => "https://example.com",
2780 "title" => "The Rock",
2782 "url" => "https://example.com/ogp",
2784 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2787 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2788 "title" => "The Rock",
2789 "type" => "video.movie",
2790 "url" => "https://example.com/ogp",
2792 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2799 |> get("/api/v1/statuses/#{activity.id}/card")
2800 |> json_response(200)
2802 assert response == card_data
2804 # works with private posts
2806 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2810 |> assign(:user, user)
2811 |> get("/api/v1/statuses/#{activity.id}/card")
2812 |> json_response(200)
2814 assert response_two == card_data
2817 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2819 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2823 |> get("/api/v1/statuses/#{activity.id}/card")
2824 |> json_response(:ok)
2826 assert response == %{
2828 "title" => "Pleroma",
2829 "description" => "",
2831 "provider_name" => "example.com",
2832 "provider_url" => "https://example.com",
2833 "url" => "https://example.com/ogp-missing-data",
2836 "title" => "Pleroma",
2837 "type" => "website",
2838 "url" => "https://example.com/ogp-missing-data"
2846 user = insert(:user)
2847 for_user = insert(:user)
2850 CommonAPI.post(user, %{
2851 "status" => "heweoo?"
2855 CommonAPI.post(user, %{
2856 "status" => "heweoo!"
2861 |> assign(:user, for_user)
2862 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2864 assert json_response(response1, 200)["bookmarked"] == true
2868 |> assign(:user, for_user)
2869 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2871 assert json_response(response2, 200)["bookmarked"] == true
2875 |> assign(:user, for_user)
2876 |> get("/api/v1/bookmarks")
2878 assert [json_response(response2, 200), json_response(response1, 200)] ==
2879 json_response(bookmarks, 200)
2883 |> assign(:user, for_user)
2884 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2886 assert json_response(response1, 200)["bookmarked"] == false
2890 |> assign(:user, for_user)
2891 |> get("/api/v1/bookmarks")
2893 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2896 describe "conversation muting" do
2898 post_user = insert(:user)
2899 user = insert(:user)
2901 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2903 [user: user, activity: activity]
2906 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2907 id_str = to_string(activity.id)
2909 assert %{"id" => ^id_str, "muted" => true} =
2911 |> assign(:user, user)
2912 |> post("/api/v1/statuses/#{activity.id}/mute")
2913 |> json_response(200)
2916 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2917 {:ok, _} = CommonAPI.add_mute(user, activity)
2921 |> assign(:user, user)
2922 |> post("/api/v1/statuses/#{activity.id}/mute")
2924 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2927 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2928 {:ok, _} = CommonAPI.add_mute(user, activity)
2930 id_str = to_string(activity.id)
2931 user = refresh_record(user)
2933 assert %{"id" => ^id_str, "muted" => false} =
2935 |> assign(:user, user)
2936 |> post("/api/v1/statuses/#{activity.id}/unmute")
2937 |> json_response(200)
2941 describe "reports" do
2943 reporter = insert(:user)
2944 target_user = insert(:user)
2946 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2948 [reporter: reporter, target_user: target_user, activity: activity]
2951 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2952 assert %{"action_taken" => false, "id" => _} =
2954 |> assign(:user, reporter)
2955 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2956 |> json_response(200)
2959 test "submit a report with statuses and comment", %{
2962 target_user: target_user,
2965 assert %{"action_taken" => false, "id" => _} =
2967 |> assign(:user, reporter)
2968 |> post("/api/v1/reports", %{
2969 "account_id" => target_user.id,
2970 "status_ids" => [activity.id],
2971 "comment" => "bad status!",
2972 "forward" => "false"
2974 |> json_response(200)
2977 test "account_id is required", %{
2982 assert %{"error" => "Valid `account_id` required"} =
2984 |> assign(:user, reporter)
2985 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2986 |> json_response(400)
2989 test "comment must be up to the size specified in the config", %{
2992 target_user: target_user
2994 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2995 comment = String.pad_trailing("a", max_size + 1, "a")
2997 error = %{"error" => "Comment must be up to #{max_size} characters"}
3001 |> assign(:user, reporter)
3002 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3003 |> json_response(400)
3006 test "returns error when account is not exist", %{
3013 |> assign(:user, reporter)
3014 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3016 assert json_response(conn, 400) == %{"error" => "Account not found"}
3020 describe "link headers" do
3021 test "preserves parameters in link headers", %{conn: conn} do
3022 user = insert(:user)
3023 other_user = insert(:user)
3026 CommonAPI.post(other_user, %{
3027 "status" => "hi @#{user.nickname}",
3028 "visibility" => "public"
3032 CommonAPI.post(other_user, %{
3033 "status" => "hi @#{user.nickname}",
3034 "visibility" => "public"
3037 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3038 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3042 |> assign(:user, user)
3043 |> get("/api/v1/notifications", %{media_only: true})
3045 assert [link_header] = get_resp_header(conn, "link")
3046 assert link_header =~ ~r/media_only=true/
3047 assert link_header =~ ~r/min_id=#{notification2.id}/
3048 assert link_header =~ ~r/max_id=#{notification1.id}/
3052 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3053 # Need to set an old-style integer ID to reproduce the problem
3054 # (these are no longer assigned to new accounts but were preserved
3055 # for existing accounts during the migration to flakeIDs)
3056 user_one = insert(:user, %{id: 1212})
3057 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3061 |> get("/api/v1/accounts/#{user_one.id}")
3065 |> get("/api/v1/accounts/#{user_two.nickname}")
3069 |> get("/api/v1/accounts/#{user_two.id}")
3071 acc_one = json_response(resp_one, 200)
3072 acc_two = json_response(resp_two, 200)
3073 acc_three = json_response(resp_three, 200)
3074 refute acc_one == acc_two
3075 assert acc_two == acc_three
3078 describe "custom emoji" do
3079 test "with tags", %{conn: conn} do
3082 |> get("/api/v1/custom_emojis")
3083 |> json_response(200)
3085 assert Map.has_key?(emoji, "shortcode")
3086 assert Map.has_key?(emoji, "static_url")
3087 assert Map.has_key?(emoji, "tags")
3088 assert is_list(emoji["tags"])
3089 assert Map.has_key?(emoji, "category")
3090 assert Map.has_key?(emoji, "url")
3091 assert Map.has_key?(emoji, "visible_in_picker")
3095 describe "index/2 redirections" do
3096 setup %{conn: conn} do
3100 signing_salt: "cooldude"
3105 |> Plug.Session.call(Plug.Session.init(session_opts))
3108 test_path = "/web/statuses/test"
3109 %{conn: conn, path: test_path}
3112 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3113 conn = get(conn, path)
3115 assert conn.status == 302
3116 assert redirected_to(conn) == "/web/login"
3119 test "redirects not logged-in users to the login page on private instances", %{
3123 Config.put([:instance, :public], false)
3125 conn = get(conn, path)
3127 assert conn.status == 302
3128 assert redirected_to(conn) == "/web/login"
3131 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3132 token = insert(:oauth_token)
3136 |> assign(:user, token.user)
3137 |> put_session(:oauth_token, token.token)
3140 assert conn.status == 200
3143 test "saves referer path to session", %{conn: conn, path: path} do
3144 conn = get(conn, path)
3145 return_to = Plug.Conn.get_session(conn, :return_to)
3147 assert return_to == path
3150 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3151 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3152 auth = insert(:oauth_authorization, app: app)
3156 |> put_session(:return_to, path)
3157 |> get("/web/login", %{code: auth.token})
3159 assert conn.status == 302
3160 assert redirected_to(conn) == path
3163 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3164 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3165 auth = insert(:oauth_authorization, app: app)
3167 conn = get(conn, "/web/login", %{code: auth.token})
3169 assert conn.status == 302
3170 assert redirected_to(conn) == "/web/getting-started"
3174 describe "scheduled activities" do
3175 test "creates a scheduled activity", %{conn: conn} do
3176 user = insert(:user)
3177 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3181 |> assign(:user, user)
3182 |> post("/api/v1/statuses", %{
3183 "status" => "scheduled",
3184 "scheduled_at" => scheduled_at
3187 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3188 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3189 assert [] == Repo.all(Activity)
3192 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3193 user = insert(:user)
3194 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3196 file = %Plug.Upload{
3197 content_type: "image/jpg",
3198 path: Path.absname("test/fixtures/image.jpg"),
3199 filename: "an_image.jpg"
3202 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3206 |> assign(:user, user)
3207 |> post("/api/v1/statuses", %{
3208 "media_ids" => [to_string(upload.id)],
3209 "status" => "scheduled",
3210 "scheduled_at" => scheduled_at
3213 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3214 assert %{"type" => "image"} = media_attachment
3217 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3219 user = insert(:user)
3222 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3226 |> assign(:user, user)
3227 |> post("/api/v1/statuses", %{
3228 "status" => "not scheduled",
3229 "scheduled_at" => scheduled_at
3232 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3233 assert [] == Repo.all(ScheduledActivity)
3236 test "returns error when daily user limit is exceeded", %{conn: conn} do
3237 user = insert(:user)
3240 NaiveDateTime.utc_now()
3241 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3242 |> NaiveDateTime.to_iso8601()
3244 attrs = %{params: %{}, scheduled_at: today}
3245 {:ok, _} = ScheduledActivity.create(user, attrs)
3246 {:ok, _} = ScheduledActivity.create(user, attrs)
3250 |> assign(:user, user)
3251 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3253 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3256 test "returns error when total user limit is exceeded", %{conn: conn} do
3257 user = insert(:user)
3260 NaiveDateTime.utc_now()
3261 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3262 |> NaiveDateTime.to_iso8601()
3265 NaiveDateTime.utc_now()
3266 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3267 |> NaiveDateTime.to_iso8601()
3269 attrs = %{params: %{}, scheduled_at: today}
3270 {:ok, _} = ScheduledActivity.create(user, attrs)
3271 {:ok, _} = ScheduledActivity.create(user, attrs)
3272 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3276 |> assign(:user, user)
3277 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3279 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3282 test "shows scheduled activities", %{conn: conn} do
3283 user = insert(:user)
3284 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3285 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3286 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3287 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3291 |> assign(:user, user)
3296 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3298 result = json_response(conn_res, 200)
3299 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3304 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3306 result = json_response(conn_res, 200)
3307 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3312 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3314 result = json_response(conn_res, 200)
3315 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3318 test "shows a scheduled activity", %{conn: conn} do
3319 user = insert(:user)
3320 scheduled_activity = insert(:scheduled_activity, user: user)
3324 |> assign(:user, user)
3325 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3327 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3328 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3332 |> assign(:user, user)
3333 |> get("/api/v1/scheduled_statuses/404")
3335 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3338 test "updates a scheduled activity", %{conn: conn} do
3339 user = insert(:user)
3340 scheduled_activity = insert(:scheduled_activity, user: user)
3343 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3347 |> assign(:user, user)
3348 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3349 scheduled_at: new_scheduled_at
3352 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3353 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3357 |> assign(:user, user)
3358 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3360 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3363 test "deletes a scheduled activity", %{conn: conn} do
3364 user = insert(:user)
3365 scheduled_activity = insert(:scheduled_activity, user: user)
3369 |> assign(:user, user)
3370 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3372 assert %{} = json_response(res_conn, 200)
3373 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3377 |> assign(:user, user)
3378 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3380 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3384 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3385 user1 = insert(:user)
3386 user2 = insert(:user)
3387 user3 = insert(:user)
3389 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3391 # Reply to status from another user
3394 |> assign(:user, user2)
3395 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3397 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3399 activity = Activity.get_by_id_with_object(id)
3401 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3402 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3404 # Reblog from the third user
3407 |> assign(:user, user3)
3408 |> post("/api/v1/statuses/#{activity.id}/reblog")
3410 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3411 json_response(conn2, 200)
3413 assert to_string(activity.id) == id
3415 # Getting third user status
3418 |> assign(:user, user3)
3419 |> get("api/v1/timelines/home")
3421 [reblogged_activity] = json_response(conn3, 200)
3423 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3425 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3426 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3429 describe "create account by app" do
3430 test "Account registration via Application", %{conn: conn} do
3433 |> post("/api/v1/apps", %{
3434 client_name: "client_name",
3435 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3436 scopes: "read, write, follow"
3440 "client_id" => client_id,
3441 "client_secret" => client_secret,
3443 "name" => "client_name",
3444 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3447 } = json_response(conn, 200)
3451 |> post("/oauth/token", %{
3452 grant_type: "client_credentials",
3453 client_id: client_id,
3454 client_secret: client_secret
3457 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3458 json_response(conn, 200)
3461 token_from_db = Repo.get_by(Token, token: token)
3462 assert token_from_db
3464 assert scope == "read write follow"
3468 |> put_req_header("authorization", "Bearer " <> token)
3469 |> post("/api/v1/accounts", %{
3471 email: "lain@example.org",
3472 password: "PlzDontHackLain",
3477 "access_token" => token,
3478 "created_at" => _created_at,
3480 "token_type" => "Bearer"
3481 } = json_response(conn, 200)
3483 token_from_db = Repo.get_by(Token, token: token)
3484 assert token_from_db
3485 token_from_db = Repo.preload(token_from_db, :user)
3486 assert token_from_db.user
3488 assert token_from_db.user.info.confirmation_pending
3491 test "rate limit", %{conn: conn} do
3492 app_token = insert(:oauth_token, user: nil)
3495 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3496 |> Map.put(:remote_ip, {15, 15, 15, 15})
3501 |> post("/api/v1/accounts", %{
3502 username: "#{i}lain",
3503 email: "#{i}lain@example.org",
3504 password: "PlzDontHackLain",
3509 "access_token" => token,
3510 "created_at" => _created_at,
3512 "token_type" => "Bearer"
3513 } = json_response(conn, 200)
3515 token_from_db = Repo.get_by(Token, token: token)
3516 assert token_from_db
3517 token_from_db = Repo.preload(token_from_db, :user)
3518 assert token_from_db.user
3520 assert token_from_db.user.info.confirmation_pending
3525 |> post("/api/v1/accounts", %{
3527 email: "6lain@example.org",
3528 password: "PlzDontHackLain",
3532 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3536 describe "GET /api/v1/polls/:id" do
3537 test "returns poll entity for object id", %{conn: conn} do
3538 user = insert(:user)
3541 CommonAPI.post(user, %{
3542 "status" => "Pleroma does",
3543 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3546 object = Object.normalize(activity)
3550 |> assign(:user, user)
3551 |> get("/api/v1/polls/#{object.id}")
3553 response = json_response(conn, 200)
3554 id = to_string(object.id)
3555 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3558 test "does not expose polls for private statuses", %{conn: conn} do
3559 user = insert(:user)
3560 other_user = insert(:user)
3563 CommonAPI.post(user, %{
3564 "status" => "Pleroma does",
3565 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3566 "visibility" => "private"
3569 object = Object.normalize(activity)
3573 |> assign(:user, other_user)
3574 |> get("/api/v1/polls/#{object.id}")
3576 assert json_response(conn, 404)
3580 describe "POST /api/v1/polls/:id/votes" do
3581 test "votes are added to the poll", %{conn: conn} do
3582 user = insert(:user)
3583 other_user = insert(:user)
3586 CommonAPI.post(user, %{
3587 "status" => "A very delicious sandwich",
3589 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3595 object = Object.normalize(activity)
3599 |> assign(:user, other_user)
3600 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3602 assert json_response(conn, 200)
3603 object = Object.get_by_id(object.id)
3605 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3610 test "author can't vote", %{conn: conn} do
3611 user = insert(:user)
3614 CommonAPI.post(user, %{
3615 "status" => "Am I cute?",
3616 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3619 object = Object.normalize(activity)
3622 |> assign(:user, user)
3623 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3624 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3626 object = Object.get_by_id(object.id)
3628 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3631 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3632 user = insert(:user)
3633 other_user = insert(:user)
3636 CommonAPI.post(user, %{
3637 "status" => "The glass is",
3638 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3641 object = Object.normalize(activity)
3644 |> assign(:user, other_user)
3645 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3646 |> json_response(422) == %{"error" => "Too many choices"}
3648 object = Object.get_by_id(object.id)
3650 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3655 test "does not allow choice index to be greater than options count", %{conn: conn} do
3656 user = insert(:user)
3657 other_user = insert(:user)
3660 CommonAPI.post(user, %{
3661 "status" => "Am I cute?",
3662 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3665 object = Object.normalize(activity)
3669 |> assign(:user, other_user)
3670 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3672 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3675 test "returns 404 error when object is not exist", %{conn: conn} do
3676 user = insert(:user)
3680 |> assign(:user, user)
3681 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3683 assert json_response(conn, 404) == %{"error" => "Record not found"}
3686 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3687 user = insert(:user)
3688 other_user = insert(:user)
3691 CommonAPI.post(user, %{
3692 "status" => "Am I cute?",
3693 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3694 "visibility" => "private"
3697 object = Object.normalize(activity)
3701 |> assign(:user, other_user)
3702 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3704 assert json_response(conn, 404) == %{"error" => "Record not found"}
3708 describe "GET /api/v1/statuses/:id/favourited_by" do
3710 user = insert(:user)
3711 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3715 |> assign(:user, user)
3717 [conn: conn, activity: activity, user: user]
3720 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3721 other_user = insert(:user)
3722 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3726 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3727 |> json_response(:ok)
3729 [%{"id" => id}] = response
3731 assert id == other_user.id
3734 test "returns empty array when status has not been favorited yet", %{
3740 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3741 |> json_response(:ok)
3743 assert Enum.empty?(response)
3746 test "does not return users who have favorited the status but are blocked", %{
3747 conn: %{assigns: %{user: user}} = conn,
3750 other_user = insert(:user)
3751 {:ok, user} = User.block(user, other_user)
3753 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3757 |> assign(:user, user)
3758 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3759 |> json_response(:ok)
3761 assert Enum.empty?(response)
3764 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3765 other_user = insert(:user)
3766 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3770 |> assign(:user, nil)
3771 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3772 |> json_response(:ok)
3774 [%{"id" => id}] = response
3775 assert id == other_user.id
3778 test "requires authentification for private posts", %{conn: conn, user: user} do
3779 other_user = insert(:user)
3782 CommonAPI.post(user, %{
3783 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3784 "visibility" => "direct"
3787 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3790 |> assign(:user, nil)
3791 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3792 |> json_response(404)
3796 |> assign(:user, other_user)
3797 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3798 |> json_response(200)
3800 [%{"id" => id}] = response
3801 assert id == other_user.id
3805 describe "GET /api/v1/statuses/:id/reblogged_by" do
3807 user = insert(:user)
3808 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3812 |> assign(:user, user)
3814 [conn: conn, activity: activity, user: user]
3817 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3818 other_user = insert(:user)
3819 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3823 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3824 |> json_response(:ok)
3826 [%{"id" => id}] = response
3828 assert id == other_user.id
3831 test "returns empty array when status has not been reblogged yet", %{
3837 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3838 |> json_response(:ok)
3840 assert Enum.empty?(response)
3843 test "does not return users who have reblogged the status but are blocked", %{
3844 conn: %{assigns: %{user: user}} = conn,
3847 other_user = insert(:user)
3848 {:ok, user} = User.block(user, other_user)
3850 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3854 |> assign(:user, user)
3855 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3856 |> json_response(:ok)
3858 assert Enum.empty?(response)
3861 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3862 other_user = insert(:user)
3863 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3867 |> assign(:user, nil)
3868 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3869 |> json_response(:ok)
3871 [%{"id" => id}] = response
3872 assert id == other_user.id
3875 test "requires authentification for private posts", %{conn: conn, user: user} do
3876 other_user = insert(:user)
3879 CommonAPI.post(user, %{
3880 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3881 "visibility" => "direct"
3885 |> assign(:user, nil)
3886 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3887 |> json_response(404)
3891 |> assign(:user, other_user)
3892 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3893 |> json_response(200)
3895 assert [] == response
3899 describe "POST /auth/password, with valid parameters" do
3900 setup %{conn: conn} do
3901 user = insert(:user)
3902 conn = post(conn, "/auth/password?email=#{user.email}")
3903 %{conn: conn, user: user}
3906 test "it returns 204", %{conn: conn} do
3907 assert json_response(conn, :no_content)
3910 test "it creates a PasswordResetToken record for user", %{user: user} do
3911 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3915 test "it sends an email to user", %{user: user} do
3916 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3918 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3919 notify_email = Config.get([:instance, :notify_email])
3920 instance_name = Config.get([:instance, :name])
3923 from: {instance_name, notify_email},
3924 to: {user.name, user.email},
3925 html_body: email.html_body
3930 describe "POST /auth/password, with invalid parameters" do
3932 user = insert(:user)
3936 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3937 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3938 assert conn.status == 404
3939 assert conn.resp_body == ""
3942 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3943 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3944 conn = post(conn, "/auth/password?email=#{user.email}")
3945 assert conn.status == 400
3946 assert conn.resp_body == ""
3950 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3952 user = insert(:user)
3953 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3957 |> Changeset.change()
3958 |> Changeset.put_embed(:info, info_change)
3961 assert user.info.confirmation_pending
3966 clear_config([:instance, :account_activation_required]) do
3967 Config.put([:instance, :account_activation_required], true)
3970 test "resend account confirmation email", %{conn: conn, user: user} do
3972 |> assign(:user, user)
3973 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3974 |> json_response(:no_content)
3976 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3977 notify_email = Config.get([:instance, :notify_email])
3978 instance_name = Config.get([:instance, :name])
3981 from: {instance_name, notify_email},
3982 to: {user.name, user.email},
3983 html_body: email.html_body
3988 describe "GET /api/v1/suggestions" do
3990 user = insert(:user)
3991 other_user = insert(:user)
3992 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3993 url500 = "http://test500?#{host}&#{user.nickname}"
3994 url200 = "http://test200?#{host}&#{user.nickname}"
3997 %{method: :get, url: ^url500} ->
3998 %Tesla.Env{status: 500, body: "bad request"}
4000 %{method: :get, url: ^url200} ->
4004 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4006 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4010 [user: user, other_user: other_user]
4013 clear_config(:suggestions)
4015 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4016 Config.put([:suggestions, :enabled], false)
4020 |> assign(:user, user)
4021 |> get("/api/v1/suggestions")
4022 |> json_response(200)
4027 test "returns error", %{conn: conn, user: user} do
4028 Config.put([:suggestions, :enabled], true)
4029 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4033 |> assign(:user, user)
4034 |> get("/api/v1/suggestions")
4035 |> json_response(500)
4037 assert res == "Something went wrong"
4040 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4041 Config.put([:suggestions, :enabled], true)
4042 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4046 |> assign(:user, user)
4047 |> get("/api/v1/suggestions")
4048 |> json_response(200)
4053 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4054 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4058 "acct" => other_user.ap_id,
4059 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4060 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4061 "id" => other_user.id