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
11 alias Pleroma.Notification
14 alias Pleroma.ScheduledActivity
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.FilterView
19 alias Pleroma.Web.OAuth.App
20 alias Pleroma.Web.OAuth.Token
21 alias Pleroma.Web.OStatus
22 alias Pleroma.Web.Push
23 alias Pleroma.Web.TwitterAPI.TwitterAPI
24 import Pleroma.Factory
25 import ExUnit.CaptureLog
27 import Swoosh.TestAssertions
29 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
32 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
36 clear_config([:instance, :public])
37 clear_config([:rich_media, :enabled])
39 test "the home timeline", %{conn: conn} do
41 following = insert(:user)
43 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
47 |> assign(:user, user)
48 |> get("/api/v1/timelines/home")
50 assert Enum.empty?(json_response(conn, 200))
52 {:ok, user} = User.follow(user, following)
56 |> assign(:user, user)
57 |> get("/api/v1/timelines/home")
59 assert [%{"content" => "test"}] = json_response(conn, 200)
62 test "the public timeline", %{conn: conn} do
63 following = insert(:user)
66 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
69 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
73 |> get("/api/v1/timelines/public", %{"local" => "False"})
75 assert length(json_response(conn, 200)) == 2
79 |> get("/api/v1/timelines/public", %{"local" => "True"})
81 assert [%{"content" => "test"}] = json_response(conn, 200)
85 |> get("/api/v1/timelines/public", %{"local" => "1"})
87 assert [%{"content" => "test"}] = json_response(conn, 200)
91 test "the public timeline when public is set to false", %{conn: conn} do
92 Config.put([:instance, :public], false)
95 |> get("/api/v1/timelines/public", %{"local" => "False"})
96 |> json_response(403) == %{"error" => "This resource requires authentication."}
99 describe "posting statuses" do
105 |> assign(:user, user)
110 test "posting a status", %{conn: conn} do
111 idempotency_key = "Pikachu rocks!"
115 |> put_req_header("idempotency-key", idempotency_key)
116 |> post("/api/v1/statuses", %{
118 "spoiler_text" => "2hu",
119 "sensitive" => "false"
122 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
124 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
126 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
127 json_response(conn_one, 200)
129 assert Activity.get_by_id(id)
133 |> put_req_header("idempotency-key", idempotency_key)
134 |> post("/api/v1/statuses", %{
136 "spoiler_text" => "2hu",
137 "sensitive" => "false"
140 assert %{"id" => second_id} = json_response(conn_two, 200)
141 assert id == second_id
145 |> post("/api/v1/statuses", %{
147 "spoiler_text" => "2hu",
148 "sensitive" => "false"
151 assert %{"id" => third_id} = json_response(conn_three, 200)
152 refute id == third_id
155 test "replying to a status", %{conn: conn} do
157 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
161 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
163 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
165 activity = Activity.get_by_id(id)
167 assert activity.data["context"] == replied_to.data["context"]
168 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
171 test "replying to a direct message with visibility other than direct", %{conn: conn} do
173 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
175 Enum.each(["public", "private", "unlisted"], fn visibility ->
178 |> post("/api/v1/statuses", %{
179 "status" => "@#{user.nickname} hey",
180 "in_reply_to_id" => replied_to.id,
181 "visibility" => visibility
184 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
188 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
191 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
193 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
194 assert Activity.get_by_id(id)
197 test "posting a sensitive status", %{conn: conn} do
200 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
202 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
203 assert Activity.get_by_id(id)
206 test "posting a fake status", %{conn: conn} do
209 |> post("/api/v1/statuses", %{
211 "\"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"
214 real_status = json_response(real_conn, 200)
217 assert Object.get_by_ap_id(real_status["uri"])
221 |> Map.put("id", nil)
222 |> Map.put("url", nil)
223 |> Map.put("uri", nil)
224 |> Map.put("created_at", nil)
225 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
229 |> post("/api/v1/statuses", %{
231 "\"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",
235 fake_status = json_response(fake_conn, 200)
238 refute Object.get_by_ap_id(fake_status["uri"])
242 |> Map.put("id", nil)
243 |> Map.put("url", nil)
244 |> Map.put("uri", nil)
245 |> Map.put("created_at", nil)
246 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
248 assert real_status == fake_status
251 test "posting a status with OGP link preview", %{conn: conn} do
252 Config.put([:rich_media, :enabled], true)
256 |> post("/api/v1/statuses", %{
257 "status" => "https://example.com/ogp"
260 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
261 assert Activity.get_by_id(id)
264 test "posting a direct status", %{conn: conn} do
265 user2 = insert(:user)
266 content = "direct cofe @#{user2.nickname}"
270 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
272 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
273 assert activity = Activity.get_by_id(id)
274 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
275 assert activity.data["to"] == [user2.ap_id]
276 assert activity.data["cc"] == []
280 describe "posting polls" do
281 test "posting a poll", %{conn: conn} do
283 time = NaiveDateTime.utc_now()
287 |> assign(:user, user)
288 |> post("/api/v1/statuses", %{
289 "status" => "Who is the #bestgrill?",
290 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
293 response = json_response(conn, 200)
295 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
296 title in ["Rei", "Asuka", "Misato"]
299 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
300 refute response["poll"]["expred"]
303 test "option limit is enforced", %{conn: conn} do
305 limit = Config.get([:instance, :poll_limits, :max_options])
309 |> assign(:user, user)
310 |> post("/api/v1/statuses", %{
312 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
315 %{"error" => error} = json_response(conn, 422)
316 assert error == "Poll can't contain more than #{limit} options"
319 test "option character limit is enforced", %{conn: conn} do
321 limit = Config.get([:instance, :poll_limits, :max_option_chars])
325 |> assign(:user, user)
326 |> post("/api/v1/statuses", %{
329 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
334 %{"error" => error} = json_response(conn, 422)
335 assert error == "Poll options cannot be longer than #{limit} characters each"
338 test "minimal date limit is enforced", %{conn: conn} do
340 limit = Config.get([:instance, :poll_limits, :min_expiration])
344 |> assign(:user, user)
345 |> post("/api/v1/statuses", %{
346 "status" => "imagine arbitrary limits",
348 "options" => ["this post was made by pleroma gang"],
349 "expires_in" => limit - 1
353 %{"error" => error} = json_response(conn, 422)
354 assert error == "Expiration date is too soon"
357 test "maximum date limit is enforced", %{conn: conn} do
359 limit = Config.get([:instance, :poll_limits, :max_expiration])
363 |> assign(:user, user)
364 |> post("/api/v1/statuses", %{
365 "status" => "imagine arbitrary limits",
367 "options" => ["this post was made by pleroma gang"],
368 "expires_in" => limit + 1
372 %{"error" => error} = json_response(conn, 422)
373 assert error == "Expiration date is too far in the future"
377 test "direct timeline", %{conn: conn} do
378 user_one = insert(:user)
379 user_two = insert(:user)
381 {:ok, user_two} = User.follow(user_two, user_one)
384 CommonAPI.post(user_one, %{
385 "status" => "Hi @#{user_two.nickname}!",
386 "visibility" => "direct"
389 {:ok, _follower_only} =
390 CommonAPI.post(user_one, %{
391 "status" => "Hi @#{user_two.nickname}!",
392 "visibility" => "private"
395 # Only direct should be visible here
398 |> assign(:user, user_two)
399 |> get("api/v1/timelines/direct")
401 [status] = json_response(res_conn, 200)
403 assert %{"visibility" => "direct"} = status
404 assert status["url"] != direct.data["id"]
406 # User should be able to see his own direct message
409 |> assign(:user, user_one)
410 |> get("api/v1/timelines/direct")
412 [status] = json_response(res_conn, 200)
414 assert %{"visibility" => "direct"} = status
416 # Both should be visible here
419 |> assign(:user, user_two)
420 |> get("api/v1/timelines/home")
422 [_s1, _s2] = json_response(res_conn, 200)
425 Enum.each(1..20, fn _ ->
427 CommonAPI.post(user_one, %{
428 "status" => "Hi @#{user_two.nickname}!",
429 "visibility" => "direct"
435 |> assign(:user, user_two)
436 |> get("api/v1/timelines/direct")
438 statuses = json_response(res_conn, 200)
439 assert length(statuses) == 20
443 |> assign(:user, user_two)
444 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
446 [status] = json_response(res_conn, 200)
448 assert status["url"] != direct.data["id"]
451 test "Conversations", %{conn: conn} do
452 user_one = insert(:user)
453 user_two = insert(:user)
454 user_three = insert(:user)
456 {:ok, user_two} = User.follow(user_two, user_one)
459 CommonAPI.post(user_one, %{
460 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
461 "visibility" => "direct"
464 {:ok, _follower_only} =
465 CommonAPI.post(user_one, %{
466 "status" => "Hi @#{user_two.nickname}!",
467 "visibility" => "private"
472 |> assign(:user, user_one)
473 |> get("/api/v1/conversations")
475 assert response = json_response(res_conn, 200)
480 "accounts" => res_accounts,
481 "last_status" => res_last_status,
486 account_ids = Enum.map(res_accounts, & &1["id"])
487 assert length(res_accounts) == 2
488 assert user_two.id in account_ids
489 assert user_three.id in account_ids
490 assert is_binary(res_id)
491 assert unread == true
492 assert res_last_status["id"] == direct.id
494 # Apparently undocumented API endpoint
497 |> assign(:user, user_one)
498 |> post("/api/v1/conversations/#{res_id}/read")
500 assert response = json_response(res_conn, 200)
501 assert length(response["accounts"]) == 2
502 assert response["last_status"]["id"] == direct.id
503 assert response["unread"] == false
505 # (vanilla) Mastodon frontend behaviour
508 |> assign(:user, user_one)
509 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
511 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
514 test "doesn't include DMs from blocked users", %{conn: conn} do
515 blocker = insert(:user)
516 blocked = insert(:user)
518 {:ok, blocker} = User.block(blocker, blocked)
520 {:ok, _blocked_direct} =
521 CommonAPI.post(blocked, %{
522 "status" => "Hi @#{blocker.nickname}!",
523 "visibility" => "direct"
527 CommonAPI.post(user, %{
528 "status" => "Hi @#{blocker.nickname}!",
529 "visibility" => "direct"
534 |> assign(:user, user)
535 |> get("api/v1/timelines/direct")
537 [status] = json_response(res_conn, 200)
538 assert status["id"] == direct.id
541 test "verify_credentials", %{conn: conn} do
546 |> assign(:user, user)
547 |> get("/api/v1/accounts/verify_credentials")
549 response = json_response(conn, 200)
551 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
552 assert response["pleroma"]["chat_token"]
553 assert id == to_string(user.id)
556 test "verify_credentials default scope unlisted", %{conn: conn} do
557 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
561 |> assign(:user, user)
562 |> get("/api/v1/accounts/verify_credentials")
564 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
565 assert id == to_string(user.id)
568 test "apps/verify_credentials", %{conn: conn} do
569 token = insert(:oauth_token)
573 |> assign(:user, token.user)
574 |> assign(:token, token)
575 |> get("/api/v1/apps/verify_credentials")
577 app = Repo.preload(token, :app).app
580 "name" => app.client_name,
581 "website" => app.website,
582 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
585 assert expected == json_response(conn, 200)
588 test "user avatar can be set", %{conn: conn} do
590 avatar_image = File.read!("test/fixtures/avatar_data_uri")
594 |> assign(:user, user)
595 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
597 user = refresh_record(user)
611 assert %{"url" => _} = json_response(conn, 200)
614 test "user avatar can be reset", %{conn: conn} do
619 |> assign(:user, user)
620 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
622 user = User.get_cached_by_id(user.id)
624 assert user.avatar == nil
626 assert %{"url" => nil} = json_response(conn, 200)
629 test "can set profile banner", %{conn: conn} do
634 |> assign(:user, user)
635 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
637 user = refresh_record(user)
638 assert user.info.banner["type"] == "Image"
640 assert %{"url" => _} = json_response(conn, 200)
643 test "can reset profile banner", %{conn: conn} do
648 |> assign(:user, user)
649 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
651 user = refresh_record(user)
652 assert user.info.banner == %{}
654 assert %{"url" => nil} = json_response(conn, 200)
657 test "background image can be set", %{conn: conn} do
662 |> assign(:user, user)
663 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
665 user = refresh_record(user)
666 assert user.info.background["type"] == "Image"
667 assert %{"url" => _} = json_response(conn, 200)
670 test "background image can be reset", %{conn: conn} do
675 |> assign(:user, user)
676 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
678 user = refresh_record(user)
679 assert user.info.background == %{}
680 assert %{"url" => nil} = json_response(conn, 200)
683 test "creates an oauth app", %{conn: conn} do
685 app_attrs = build(:oauth_app)
689 |> assign(:user, user)
690 |> post("/api/v1/apps", %{
691 client_name: app_attrs.client_name,
692 redirect_uris: app_attrs.redirect_uris
695 [app] = Repo.all(App)
698 "name" => app.client_name,
699 "website" => app.website,
700 "client_id" => app.client_id,
701 "client_secret" => app.client_secret,
702 "id" => app.id |> to_string(),
703 "redirect_uri" => app.redirect_uris,
704 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
707 assert expected == json_response(conn, 200)
710 test "get a status", %{conn: conn} do
711 activity = insert(:note_activity)
715 |> get("/api/v1/statuses/#{activity.id}")
717 assert %{"id" => id} = json_response(conn, 200)
718 assert id == to_string(activity.id)
721 describe "deleting a status" do
722 test "when you created it", %{conn: conn} do
723 activity = insert(:note_activity)
724 author = User.get_cached_by_ap_id(activity.data["actor"])
728 |> assign(:user, author)
729 |> delete("/api/v1/statuses/#{activity.id}")
731 assert %{} = json_response(conn, 200)
733 refute Activity.get_by_id(activity.id)
736 test "when you didn't create it", %{conn: conn} do
737 activity = insert(:note_activity)
742 |> assign(:user, user)
743 |> delete("/api/v1/statuses/#{activity.id}")
745 assert %{"error" => _} = json_response(conn, 403)
747 assert Activity.get_by_id(activity.id) == activity
750 test "when you're an admin or moderator", %{conn: conn} do
751 activity1 = insert(:note_activity)
752 activity2 = insert(:note_activity)
753 admin = insert(:user, info: %{is_admin: true})
754 moderator = insert(:user, info: %{is_moderator: true})
758 |> assign(:user, admin)
759 |> delete("/api/v1/statuses/#{activity1.id}")
761 assert %{} = json_response(res_conn, 200)
765 |> assign(:user, moderator)
766 |> delete("/api/v1/statuses/#{activity2.id}")
768 assert %{} = json_response(res_conn, 200)
770 refute Activity.get_by_id(activity1.id)
771 refute Activity.get_by_id(activity2.id)
775 describe "filters" do
776 test "creating a filter", %{conn: conn} do
779 filter = %Pleroma.Filter{
786 |> assign(:user, user)
787 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
789 assert response = json_response(conn, 200)
790 assert response["phrase"] == filter.phrase
791 assert response["context"] == filter.context
792 assert response["irreversible"] == false
793 assert response["id"] != nil
794 assert response["id"] != ""
797 test "fetching a list of filters", %{conn: conn} do
800 query_one = %Pleroma.Filter{
807 query_two = %Pleroma.Filter{
814 {:ok, filter_one} = Pleroma.Filter.create(query_one)
815 {:ok, filter_two} = Pleroma.Filter.create(query_two)
819 |> assign(:user, user)
820 |> get("/api/v1/filters")
821 |> json_response(200)
827 filters: [filter_two, filter_one]
831 test "get a filter", %{conn: conn} do
834 query = %Pleroma.Filter{
841 {:ok, filter} = Pleroma.Filter.create(query)
845 |> assign(:user, user)
846 |> get("/api/v1/filters/#{filter.filter_id}")
848 assert _response = json_response(conn, 200)
851 test "update a filter", %{conn: conn} do
854 query = %Pleroma.Filter{
861 {:ok, _filter} = Pleroma.Filter.create(query)
863 new = %Pleroma.Filter{
870 |> assign(:user, user)
871 |> put("/api/v1/filters/#{query.filter_id}", %{
876 assert response = json_response(conn, 200)
877 assert response["phrase"] == new.phrase
878 assert response["context"] == new.context
881 test "delete a filter", %{conn: conn} do
884 query = %Pleroma.Filter{
891 {:ok, filter} = Pleroma.Filter.create(query)
895 |> assign(:user, user)
896 |> delete("/api/v1/filters/#{filter.filter_id}")
898 assert response = json_response(conn, 200)
899 assert response == %{}
904 test "creating a list", %{conn: conn} do
909 |> assign(:user, user)
910 |> post("/api/v1/lists", %{"title" => "cuties"})
912 assert %{"title" => title} = json_response(conn, 200)
913 assert title == "cuties"
916 test "adding users to a list", %{conn: conn} do
918 other_user = insert(:user)
919 {:ok, list} = Pleroma.List.create("name", user)
923 |> assign(:user, user)
924 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
926 assert %{} == json_response(conn, 200)
927 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
928 assert following == [other_user.follower_address]
931 test "removing users from a list", %{conn: conn} do
933 other_user = insert(:user)
934 third_user = insert(:user)
935 {:ok, list} = Pleroma.List.create("name", user)
936 {:ok, list} = Pleroma.List.follow(list, other_user)
937 {:ok, list} = Pleroma.List.follow(list, third_user)
941 |> assign(:user, user)
942 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
944 assert %{} == json_response(conn, 200)
945 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
946 assert following == [third_user.follower_address]
949 test "listing users in a list", %{conn: conn} do
951 other_user = insert(:user)
952 {:ok, list} = Pleroma.List.create("name", user)
953 {:ok, list} = Pleroma.List.follow(list, other_user)
957 |> assign(:user, user)
958 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
960 assert [%{"id" => id}] = json_response(conn, 200)
961 assert id == to_string(other_user.id)
964 test "retrieving a list", %{conn: conn} do
966 {:ok, list} = Pleroma.List.create("name", user)
970 |> assign(:user, user)
971 |> get("/api/v1/lists/#{list.id}")
973 assert %{"id" => id} = json_response(conn, 200)
974 assert id == to_string(list.id)
977 test "renaming a list", %{conn: conn} do
979 {:ok, list} = Pleroma.List.create("name", user)
983 |> assign(:user, user)
984 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
986 assert %{"title" => name} = json_response(conn, 200)
987 assert name == "newname"
990 test "deleting a list", %{conn: conn} do
992 {:ok, list} = Pleroma.List.create("name", user)
996 |> assign(:user, user)
997 |> delete("/api/v1/lists/#{list.id}")
999 assert %{} = json_response(conn, 200)
1000 assert is_nil(Repo.get(Pleroma.List, list.id))
1003 test "list timeline", %{conn: conn} do
1004 user = insert(:user)
1005 other_user = insert(:user)
1006 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
1007 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1008 {:ok, list} = Pleroma.List.create("name", user)
1009 {:ok, list} = Pleroma.List.follow(list, other_user)
1013 |> assign(:user, user)
1014 |> get("/api/v1/timelines/list/#{list.id}")
1016 assert [%{"id" => id}] = json_response(conn, 200)
1018 assert id == to_string(activity_two.id)
1021 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1022 user = insert(:user)
1023 other_user = insert(:user)
1024 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1026 {:ok, _activity_two} =
1027 CommonAPI.post(other_user, %{
1028 "status" => "Marisa is cute.",
1029 "visibility" => "private"
1032 {:ok, list} = Pleroma.List.create("name", user)
1033 {:ok, list} = Pleroma.List.follow(list, other_user)
1037 |> assign(:user, user)
1038 |> get("/api/v1/timelines/list/#{list.id}")
1040 assert [%{"id" => id}] = json_response(conn, 200)
1042 assert id == to_string(activity_one.id)
1046 describe "notifications" do
1047 test "list of notifications", %{conn: conn} do
1048 user = insert(:user)
1049 other_user = insert(:user)
1051 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1053 {:ok, [_notification]} = Notification.create_notifications(activity)
1057 |> assign(:user, user)
1058 |> get("/api/v1/notifications")
1061 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1063 }\">@<span>#{user.nickname}</span></a></span>"
1065 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1066 assert response == expected_response
1069 test "getting a single notification", %{conn: conn} do
1070 user = insert(:user)
1071 other_user = insert(:user)
1073 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1075 {:ok, [notification]} = Notification.create_notifications(activity)
1079 |> assign(:user, user)
1080 |> get("/api/v1/notifications/#{notification.id}")
1083 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1085 }\">@<span>#{user.nickname}</span></a></span>"
1087 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1088 assert response == expected_response
1091 test "dismissing a single notification", %{conn: conn} do
1092 user = insert(:user)
1093 other_user = insert(:user)
1095 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1097 {:ok, [notification]} = Notification.create_notifications(activity)
1101 |> assign(:user, user)
1102 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1104 assert %{} = json_response(conn, 200)
1107 test "clearing all notifications", %{conn: conn} do
1108 user = insert(:user)
1109 other_user = insert(:user)
1111 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1113 {:ok, [_notification]} = Notification.create_notifications(activity)
1117 |> assign(:user, user)
1118 |> post("/api/v1/notifications/clear")
1120 assert %{} = json_response(conn, 200)
1124 |> assign(:user, user)
1125 |> get("/api/v1/notifications")
1127 assert all = json_response(conn, 200)
1131 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1132 user = insert(:user)
1133 other_user = insert(:user)
1135 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1136 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1137 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1138 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1140 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1141 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1142 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1143 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1147 |> assign(:user, user)
1152 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1154 result = json_response(conn_res, 200)
1155 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1160 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1162 result = json_response(conn_res, 200)
1163 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1168 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1170 result = json_response(conn_res, 200)
1171 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1174 test "filters notifications using exclude_types", %{conn: conn} do
1175 user = insert(:user)
1176 other_user = insert(:user)
1178 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1179 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1180 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1181 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1182 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1184 mention_notification_id =
1185 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1187 favorite_notification_id =
1188 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1190 reblog_notification_id =
1191 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1193 follow_notification_id =
1194 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1198 |> assign(:user, user)
1201 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1203 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1206 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1208 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1211 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1213 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1216 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1218 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1221 test "destroy multiple", %{conn: conn} do
1222 user = insert(:user)
1223 other_user = insert(:user)
1225 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1226 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1227 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1228 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1230 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1231 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1232 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1233 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1237 |> assign(:user, user)
1241 |> get("/api/v1/notifications")
1243 result = json_response(conn_res, 200)
1244 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1248 |> assign(:user, other_user)
1252 |> get("/api/v1/notifications")
1254 result = json_response(conn_res, 200)
1255 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1259 |> delete("/api/v1/notifications/destroy_multiple", %{
1260 "ids" => [notification1_id, notification2_id]
1263 assert json_response(conn_destroy, 200) == %{}
1267 |> get("/api/v1/notifications")
1269 result = json_response(conn_res, 200)
1270 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1273 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1274 user = insert(:user)
1275 user2 = insert(:user)
1277 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1278 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1280 conn = assign(conn, :user, user)
1282 conn = get(conn, "/api/v1/notifications")
1284 assert length(json_response(conn, 200)) == 1
1286 {:ok, user} = User.mute(user, user2)
1288 conn = assign(build_conn(), :user, user)
1289 conn = get(conn, "/api/v1/notifications")
1291 assert json_response(conn, 200) == []
1294 test "see notifications after muting user without notifications", %{conn: conn} do
1295 user = insert(:user)
1296 user2 = insert(:user)
1298 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1299 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1301 conn = assign(conn, :user, user)
1303 conn = get(conn, "/api/v1/notifications")
1305 assert length(json_response(conn, 200)) == 1
1307 {:ok, user} = User.mute(user, user2, false)
1309 conn = assign(build_conn(), :user, user)
1310 conn = get(conn, "/api/v1/notifications")
1312 assert length(json_response(conn, 200)) == 1
1315 test "see notifications after muting user with notifications and with_muted parameter", %{
1318 user = insert(:user)
1319 user2 = insert(:user)
1321 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1322 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1324 conn = assign(conn, :user, user)
1326 conn = get(conn, "/api/v1/notifications")
1328 assert length(json_response(conn, 200)) == 1
1330 {:ok, user} = User.mute(user, user2)
1332 conn = assign(build_conn(), :user, user)
1333 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1335 assert length(json_response(conn, 200)) == 1
1339 describe "reblogging" do
1340 test "reblogs and returns the reblogged status", %{conn: conn} do
1341 activity = insert(:note_activity)
1342 user = insert(:user)
1346 |> assign(:user, user)
1347 |> post("/api/v1/statuses/#{activity.id}/reblog")
1350 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1352 } = json_response(conn, 200)
1354 assert to_string(activity.id) == id
1357 test "reblogged status for another user", %{conn: conn} do
1358 activity = insert(:note_activity)
1359 user1 = insert(:user)
1360 user2 = insert(:user)
1361 user3 = insert(:user)
1362 CommonAPI.favorite(activity.id, user2)
1363 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1364 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1365 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1369 |> assign(:user, user3)
1370 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1373 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1374 "reblogged" => false,
1375 "favourited" => false,
1376 "bookmarked" => false
1377 } = json_response(conn_res, 200)
1381 |> assign(:user, user2)
1382 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1385 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1386 "reblogged" => true,
1387 "favourited" => true,
1388 "bookmarked" => true
1389 } = json_response(conn_res, 200)
1391 assert to_string(activity.id) == id
1394 test "returns 400 error when activity is not exist", %{conn: conn} do
1395 user = insert(:user)
1399 |> assign(:user, user)
1400 |> post("/api/v1/statuses/foo/reblog")
1402 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1406 describe "unreblogging" do
1407 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1408 activity = insert(:note_activity)
1409 user = insert(:user)
1411 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1415 |> assign(:user, user)
1416 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1418 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1420 assert to_string(activity.id) == id
1423 test "returns 400 error when activity is not exist", %{conn: conn} do
1424 user = insert(:user)
1428 |> assign(:user, user)
1429 |> post("/api/v1/statuses/foo/unreblog")
1431 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1435 describe "favoriting" do
1436 test "favs a status and returns it", %{conn: conn} do
1437 activity = insert(:note_activity)
1438 user = insert(:user)
1442 |> assign(:user, user)
1443 |> post("/api/v1/statuses/#{activity.id}/favourite")
1445 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1446 json_response(conn, 200)
1448 assert to_string(activity.id) == id
1451 test "returns 400 error for a wrong id", %{conn: conn} do
1452 user = insert(:user)
1456 |> assign(:user, user)
1457 |> post("/api/v1/statuses/1/favourite")
1459 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1463 describe "unfavoriting" do
1464 test "unfavorites a status and returns it", %{conn: conn} do
1465 activity = insert(:note_activity)
1466 user = insert(:user)
1468 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1472 |> assign(:user, user)
1473 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1475 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1476 json_response(conn, 200)
1478 assert to_string(activity.id) == id
1481 test "returns 400 error for a wrong id", %{conn: conn} do
1482 user = insert(:user)
1486 |> assign(:user, user)
1487 |> post("/api/v1/statuses/1/unfavourite")
1489 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1493 describe "user timelines" do
1494 test "gets a users statuses", %{conn: conn} do
1495 user_one = insert(:user)
1496 user_two = insert(:user)
1497 user_three = insert(:user)
1499 {:ok, user_three} = User.follow(user_three, user_one)
1501 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1503 {:ok, direct_activity} =
1504 CommonAPI.post(user_one, %{
1505 "status" => "Hi, @#{user_two.nickname}.",
1506 "visibility" => "direct"
1509 {:ok, private_activity} =
1510 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1514 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1516 assert [%{"id" => id}] = json_response(resp, 200)
1517 assert id == to_string(activity.id)
1521 |> assign(:user, user_two)
1522 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1524 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1525 assert id_one == to_string(direct_activity.id)
1526 assert id_two == to_string(activity.id)
1530 |> assign(:user, user_three)
1531 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1533 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1534 assert id_one == to_string(private_activity.id)
1535 assert id_two == to_string(activity.id)
1538 test "unimplemented pinned statuses feature", %{conn: conn} do
1539 note = insert(:note_activity)
1540 user = User.get_cached_by_ap_id(note.data["actor"])
1544 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1546 assert json_response(conn, 200) == []
1549 test "gets an users media", %{conn: conn} do
1550 note = insert(:note_activity)
1551 user = User.get_cached_by_ap_id(note.data["actor"])
1553 file = %Plug.Upload{
1554 content_type: "image/jpg",
1555 path: Path.absname("test/fixtures/image.jpg"),
1556 filename: "an_image.jpg"
1560 TwitterAPI.upload(file, user, "json")
1564 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1568 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1570 assert [%{"id" => id}] = json_response(conn, 200)
1571 assert id == to_string(image_post.id)
1575 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1577 assert [%{"id" => id}] = json_response(conn, 200)
1578 assert id == to_string(image_post.id)
1581 test "gets a user's statuses without reblogs", %{conn: conn} do
1582 user = insert(:user)
1583 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1584 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1588 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1590 assert [%{"id" => id}] = json_response(conn, 200)
1591 assert id == to_string(post.id)
1595 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1597 assert [%{"id" => id}] = json_response(conn, 200)
1598 assert id == to_string(post.id)
1601 test "filters user's statuses by a hashtag", %{conn: conn} do
1602 user = insert(:user)
1603 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1604 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1608 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1610 assert [%{"id" => id}] = json_response(conn, 200)
1611 assert id == to_string(post.id)
1615 describe "user relationships" do
1616 test "returns the relationships for the current user", %{conn: conn} do
1617 user = insert(:user)
1618 other_user = insert(:user)
1619 {:ok, user} = User.follow(user, other_user)
1623 |> assign(:user, user)
1624 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1626 assert [relationship] = json_response(conn, 200)
1628 assert to_string(other_user.id) == relationship["id"]
1632 describe "media upload" do
1634 user = insert(:user)
1638 |> assign(:user, user)
1640 image = %Plug.Upload{
1641 content_type: "image/jpg",
1642 path: Path.absname("test/fixtures/image.jpg"),
1643 filename: "an_image.jpg"
1646 [conn: conn, image: image]
1649 clear_config([:media_proxy])
1650 clear_config([Pleroma.Upload])
1652 test "returns uploaded image", %{conn: conn, image: image} do
1653 desc = "Description of the image"
1657 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1658 |> json_response(:ok)
1660 assert media["type"] == "image"
1661 assert media["description"] == desc
1664 object = Repo.get(Object, media["id"])
1665 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1669 describe "locked accounts" do
1670 test "/api/v1/follow_requests works" do
1671 user = insert(:user, %{info: %User.Info{locked: true}})
1672 other_user = insert(:user)
1674 {:ok, _activity} = ActivityPub.follow(other_user, user)
1676 user = User.get_cached_by_id(user.id)
1677 other_user = User.get_cached_by_id(other_user.id)
1679 assert User.following?(other_user, user) == false
1683 |> assign(:user, user)
1684 |> get("/api/v1/follow_requests")
1686 assert [relationship] = json_response(conn, 200)
1687 assert to_string(other_user.id) == relationship["id"]
1690 test "/api/v1/follow_requests/:id/authorize works" do
1691 user = insert(:user, %{info: %User.Info{locked: true}})
1692 other_user = insert(:user)
1694 {:ok, _activity} = ActivityPub.follow(other_user, user)
1696 user = User.get_cached_by_id(user.id)
1697 other_user = User.get_cached_by_id(other_user.id)
1699 assert User.following?(other_user, user) == false
1703 |> assign(:user, user)
1704 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1706 assert relationship = json_response(conn, 200)
1707 assert to_string(other_user.id) == relationship["id"]
1709 user = User.get_cached_by_id(user.id)
1710 other_user = User.get_cached_by_id(other_user.id)
1712 assert User.following?(other_user, user) == true
1715 test "verify_credentials", %{conn: conn} do
1716 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1720 |> assign(:user, user)
1721 |> get("/api/v1/accounts/verify_credentials")
1723 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1724 assert id == to_string(user.id)
1727 test "/api/v1/follow_requests/:id/reject works" do
1728 user = insert(:user, %{info: %User.Info{locked: true}})
1729 other_user = insert(:user)
1731 {:ok, _activity} = ActivityPub.follow(other_user, user)
1733 user = User.get_cached_by_id(user.id)
1737 |> assign(:user, user)
1738 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1740 assert relationship = json_response(conn, 200)
1741 assert to_string(other_user.id) == relationship["id"]
1743 user = User.get_cached_by_id(user.id)
1744 other_user = User.get_cached_by_id(other_user.id)
1746 assert User.following?(other_user, user) == false
1750 test "account fetching", %{conn: conn} do
1751 user = insert(:user)
1755 |> get("/api/v1/accounts/#{user.id}")
1757 assert %{"id" => id} = json_response(conn, 200)
1758 assert id == to_string(user.id)
1762 |> get("/api/v1/accounts/-1")
1764 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1767 test "account fetching also works nickname", %{conn: conn} do
1768 user = insert(:user)
1772 |> get("/api/v1/accounts/#{user.nickname}")
1774 assert %{"id" => id} = json_response(conn, 200)
1775 assert id == user.id
1778 test "mascot upload", %{conn: conn} do
1779 user = insert(:user)
1781 non_image_file = %Plug.Upload{
1782 content_type: "audio/mpeg",
1783 path: Path.absname("test/fixtures/sound.mp3"),
1784 filename: "sound.mp3"
1789 |> assign(:user, user)
1790 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1792 assert json_response(conn, 415)
1794 file = %Plug.Upload{
1795 content_type: "image/jpg",
1796 path: Path.absname("test/fixtures/image.jpg"),
1797 filename: "an_image.jpg"
1802 |> assign(:user, user)
1803 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1805 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1808 test "mascot retrieving", %{conn: conn} do
1809 user = insert(:user)
1810 # When user hasn't set a mascot, we should just get pleroma tan back
1813 |> assign(:user, user)
1814 |> get("/api/v1/pleroma/mascot")
1816 assert %{"url" => url} = json_response(conn, 200)
1817 assert url =~ "pleroma-fox-tan-smol"
1819 # When a user sets their mascot, we should get that back
1820 file = %Plug.Upload{
1821 content_type: "image/jpg",
1822 path: Path.absname("test/fixtures/image.jpg"),
1823 filename: "an_image.jpg"
1828 |> assign(:user, user)
1829 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1831 assert json_response(conn, 200)
1833 user = User.get_cached_by_id(user.id)
1837 |> assign(:user, user)
1838 |> get("/api/v1/pleroma/mascot")
1840 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1841 assert url =~ "an_image"
1844 test "hashtag timeline", %{conn: conn} do
1845 following = insert(:user)
1848 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1850 {:ok, [_activity]} =
1851 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1855 |> get("/api/v1/timelines/tag/2hu")
1857 assert [%{"id" => id}] = json_response(nconn, 200)
1859 assert id == to_string(activity.id)
1861 # works for different capitalization too
1864 |> get("/api/v1/timelines/tag/2HU")
1866 assert [%{"id" => id}] = json_response(nconn, 200)
1868 assert id == to_string(activity.id)
1872 test "multi-hashtag timeline", %{conn: conn} do
1873 user = insert(:user)
1875 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1876 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1877 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1881 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1883 [status_none, status_test1, status_test] = json_response(any_test, 200)
1885 assert to_string(activity_test.id) == status_test["id"]
1886 assert to_string(activity_test1.id) == status_test1["id"]
1887 assert to_string(activity_none.id) == status_none["id"]
1891 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1893 assert [status_test1] == json_response(restricted_test, 200)
1895 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1897 assert [status_none] == json_response(all_test, 200)
1900 test "getting followers", %{conn: conn} do
1901 user = insert(:user)
1902 other_user = insert(:user)
1903 {:ok, user} = User.follow(user, other_user)
1907 |> get("/api/v1/accounts/#{other_user.id}/followers")
1909 assert [%{"id" => id}] = json_response(conn, 200)
1910 assert id == to_string(user.id)
1913 test "getting followers, hide_followers", %{conn: conn} do
1914 user = insert(:user)
1915 other_user = insert(:user, %{info: %{hide_followers: true}})
1916 {:ok, _user} = User.follow(user, other_user)
1920 |> get("/api/v1/accounts/#{other_user.id}/followers")
1922 assert [] == json_response(conn, 200)
1925 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1926 user = insert(:user)
1927 other_user = insert(:user, %{info: %{hide_followers: true}})
1928 {:ok, _user} = User.follow(user, other_user)
1932 |> assign(:user, other_user)
1933 |> get("/api/v1/accounts/#{other_user.id}/followers")
1935 refute [] == json_response(conn, 200)
1938 test "getting followers, pagination", %{conn: conn} do
1939 user = insert(:user)
1940 follower1 = insert(:user)
1941 follower2 = insert(:user)
1942 follower3 = insert(:user)
1943 {:ok, _} = User.follow(follower1, user)
1944 {:ok, _} = User.follow(follower2, user)
1945 {:ok, _} = User.follow(follower3, user)
1949 |> assign(:user, user)
1953 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1955 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1956 assert id3 == follower3.id
1957 assert id2 == follower2.id
1961 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1963 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1964 assert id2 == follower2.id
1965 assert id1 == follower1.id
1969 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1971 assert [%{"id" => id2}] = json_response(res_conn, 200)
1972 assert id2 == follower2.id
1974 assert [link_header] = get_resp_header(res_conn, "link")
1975 assert link_header =~ ~r/min_id=#{follower2.id}/
1976 assert link_header =~ ~r/max_id=#{follower2.id}/
1979 test "getting following", %{conn: conn} do
1980 user = insert(:user)
1981 other_user = insert(:user)
1982 {:ok, user} = User.follow(user, other_user)
1986 |> get("/api/v1/accounts/#{user.id}/following")
1988 assert [%{"id" => id}] = json_response(conn, 200)
1989 assert id == to_string(other_user.id)
1992 test "getting following, hide_follows", %{conn: conn} do
1993 user = insert(:user, %{info: %{hide_follows: true}})
1994 other_user = insert(:user)
1995 {:ok, user} = User.follow(user, other_user)
1999 |> get("/api/v1/accounts/#{user.id}/following")
2001 assert [] == json_response(conn, 200)
2004 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2005 user = insert(:user, %{info: %{hide_follows: true}})
2006 other_user = insert(:user)
2007 {:ok, user} = User.follow(user, other_user)
2011 |> assign(:user, user)
2012 |> get("/api/v1/accounts/#{user.id}/following")
2014 refute [] == json_response(conn, 200)
2017 test "getting following, pagination", %{conn: conn} do
2018 user = insert(:user)
2019 following1 = insert(:user)
2020 following2 = insert(:user)
2021 following3 = insert(:user)
2022 {:ok, _} = User.follow(user, following1)
2023 {:ok, _} = User.follow(user, following2)
2024 {:ok, _} = User.follow(user, following3)
2028 |> assign(:user, user)
2032 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2034 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2035 assert id3 == following3.id
2036 assert id2 == following2.id
2040 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2042 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2043 assert id2 == following2.id
2044 assert id1 == following1.id
2048 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2050 assert [%{"id" => id2}] = json_response(res_conn, 200)
2051 assert id2 == following2.id
2053 assert [link_header] = get_resp_header(res_conn, "link")
2054 assert link_header =~ ~r/min_id=#{following2.id}/
2055 assert link_header =~ ~r/max_id=#{following2.id}/
2058 test "following / unfollowing a user", %{conn: conn} do
2059 user = insert(:user)
2060 other_user = insert(:user)
2064 |> assign(:user, user)
2065 |> post("/api/v1/accounts/#{other_user.id}/follow")
2067 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2069 user = User.get_cached_by_id(user.id)
2073 |> assign(:user, user)
2074 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2076 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2078 user = User.get_cached_by_id(user.id)
2082 |> assign(:user, user)
2083 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2085 assert %{"id" => id} = json_response(conn, 200)
2086 assert id == to_string(other_user.id)
2089 test "following without reblogs" do
2090 follower = insert(:user)
2091 followed = insert(:user)
2092 other_user = insert(:user)
2096 |> assign(:user, follower)
2097 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2099 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2101 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2102 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2106 |> assign(:user, User.get_cached_by_id(follower.id))
2107 |> get("/api/v1/timelines/home")
2109 assert [] == json_response(conn, 200)
2113 |> assign(:user, follower)
2114 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2116 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2120 |> assign(:user, User.get_cached_by_id(follower.id))
2121 |> get("/api/v1/timelines/home")
2123 expected_activity_id = reblog.id
2124 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2127 test "following / unfollowing errors" do
2128 user = insert(:user)
2132 |> assign(:user, user)
2135 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2136 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2139 user = User.get_cached_by_id(user.id)
2140 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2141 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2143 # self follow via uri
2144 user = User.get_cached_by_id(user.id)
2145 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2146 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2148 # follow non existing user
2149 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2150 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2152 # follow non existing user via uri
2153 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2154 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2156 # unfollow non existing user
2157 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2158 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2161 describe "mute/unmute" do
2162 test "with notifications", %{conn: conn} do
2163 user = insert(:user)
2164 other_user = insert(:user)
2168 |> assign(:user, user)
2169 |> post("/api/v1/accounts/#{other_user.id}/mute")
2171 response = json_response(conn, 200)
2173 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2174 user = User.get_cached_by_id(user.id)
2178 |> assign(:user, user)
2179 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2181 response = json_response(conn, 200)
2182 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2185 test "without notifications", %{conn: conn} do
2186 user = insert(:user)
2187 other_user = insert(:user)
2191 |> assign(:user, user)
2192 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2194 response = json_response(conn, 200)
2196 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2197 user = User.get_cached_by_id(user.id)
2201 |> assign(:user, user)
2202 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2204 response = json_response(conn, 200)
2205 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2209 test "subscribing / unsubscribing to a user", %{conn: conn} do
2210 user = insert(:user)
2211 subscription_target = insert(:user)
2215 |> assign(:user, user)
2216 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2218 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2222 |> assign(:user, user)
2223 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2225 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2228 test "getting a list of mutes", %{conn: conn} do
2229 user = insert(:user)
2230 other_user = insert(:user)
2232 {:ok, user} = User.mute(user, other_user)
2236 |> assign(:user, user)
2237 |> get("/api/v1/mutes")
2239 other_user_id = to_string(other_user.id)
2240 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2243 test "blocking / unblocking a user", %{conn: conn} do
2244 user = insert(:user)
2245 other_user = insert(:user)
2249 |> assign(:user, user)
2250 |> post("/api/v1/accounts/#{other_user.id}/block")
2252 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2254 user = User.get_cached_by_id(user.id)
2258 |> assign(:user, user)
2259 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2261 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2264 test "getting a list of blocks", %{conn: conn} do
2265 user = insert(:user)
2266 other_user = insert(:user)
2268 {:ok, user} = User.block(user, other_user)
2272 |> assign(:user, user)
2273 |> get("/api/v1/blocks")
2275 other_user_id = to_string(other_user.id)
2276 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2279 test "blocking / unblocking a domain", %{conn: conn} do
2280 user = insert(:user)
2281 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2285 |> assign(:user, user)
2286 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2288 assert %{} = json_response(conn, 200)
2289 user = User.get_cached_by_ap_id(user.ap_id)
2290 assert User.blocks?(user, other_user)
2294 |> assign(:user, user)
2295 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2297 assert %{} = json_response(conn, 200)
2298 user = User.get_cached_by_ap_id(user.ap_id)
2299 refute User.blocks?(user, other_user)
2302 test "getting a list of domain blocks", %{conn: conn} do
2303 user = insert(:user)
2305 {:ok, user} = User.block_domain(user, "bad.site")
2306 {:ok, user} = User.block_domain(user, "even.worse.site")
2310 |> assign(:user, user)
2311 |> get("/api/v1/domain_blocks")
2313 domain_blocks = json_response(conn, 200)
2315 assert "bad.site" in domain_blocks
2316 assert "even.worse.site" in domain_blocks
2319 test "unimplemented follow_requests, blocks, domain blocks" do
2320 user = insert(:user)
2322 ["blocks", "domain_blocks", "follow_requests"]
2323 |> Enum.each(fn endpoint ->
2326 |> assign(:user, user)
2327 |> get("/api/v1/#{endpoint}")
2329 assert [] = json_response(conn, 200)
2333 test "returns the favorites of a user", %{conn: conn} do
2334 user = insert(:user)
2335 other_user = insert(:user)
2337 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2338 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2340 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2344 |> assign(:user, user)
2345 |> get("/api/v1/favourites")
2347 assert [status] = json_response(first_conn, 200)
2348 assert status["id"] == to_string(activity.id)
2350 assert [{"link", _link_header}] =
2351 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2353 # Honours query params
2354 {:ok, second_activity} =
2355 CommonAPI.post(other_user, %{
2357 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2360 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2362 last_like = status["id"]
2366 |> assign(:user, user)
2367 |> get("/api/v1/favourites?since_id=#{last_like}")
2369 assert [second_status] = json_response(second_conn, 200)
2370 assert second_status["id"] == to_string(second_activity.id)
2374 |> assign(:user, user)
2375 |> get("/api/v1/favourites?limit=0")
2377 assert [] = json_response(third_conn, 200)
2380 describe "getting favorites timeline of specified user" do
2382 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2383 [current_user: current_user, user: user]
2386 test "returns list of statuses favorited by specified user", %{
2388 current_user: current_user,
2391 [activity | _] = insert_pair(:note_activity)
2392 CommonAPI.favorite(activity.id, user)
2396 |> assign(:user, current_user)
2397 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2398 |> json_response(:ok)
2402 assert length(response) == 1
2403 assert like["id"] == activity.id
2406 test "returns favorites for specified user_id when user is not logged in", %{
2410 activity = insert(:note_activity)
2411 CommonAPI.favorite(activity.id, user)
2415 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2416 |> json_response(:ok)
2418 assert length(response) == 1
2421 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2423 current_user: current_user,
2427 CommonAPI.post(current_user, %{
2428 "status" => "Hi @#{user.nickname}!",
2429 "visibility" => "direct"
2432 CommonAPI.favorite(direct.id, user)
2436 |> assign(:user, current_user)
2437 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2438 |> json_response(:ok)
2440 assert length(response) == 1
2442 anonymous_response =
2444 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2445 |> json_response(:ok)
2447 assert Enum.empty?(anonymous_response)
2450 test "does not return others' favorited DM when user is not one of recipients", %{
2452 current_user: current_user,
2455 user_two = insert(:user)
2458 CommonAPI.post(user_two, %{
2459 "status" => "Hi @#{user.nickname}!",
2460 "visibility" => "direct"
2463 CommonAPI.favorite(direct.id, user)
2467 |> assign(:user, current_user)
2468 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2469 |> json_response(:ok)
2471 assert Enum.empty?(response)
2474 test "paginates favorites using since_id and max_id", %{
2476 current_user: current_user,
2479 activities = insert_list(10, :note_activity)
2481 Enum.each(activities, fn activity ->
2482 CommonAPI.favorite(activity.id, user)
2485 third_activity = Enum.at(activities, 2)
2486 seventh_activity = Enum.at(activities, 6)
2490 |> assign(:user, current_user)
2491 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2492 since_id: third_activity.id,
2493 max_id: seventh_activity.id
2495 |> json_response(:ok)
2497 assert length(response) == 3
2498 refute third_activity in response
2499 refute seventh_activity in response
2502 test "limits favorites using limit parameter", %{
2504 current_user: current_user,
2508 |> insert_list(:note_activity)
2509 |> Enum.each(fn activity ->
2510 CommonAPI.favorite(activity.id, user)
2515 |> assign(:user, current_user)
2516 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2517 |> json_response(:ok)
2519 assert length(response) == 3
2522 test "returns empty response when user does not have any favorited statuses", %{
2524 current_user: current_user,
2529 |> assign(:user, current_user)
2530 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2531 |> json_response(:ok)
2533 assert Enum.empty?(response)
2536 test "returns 404 error when specified user is not exist", %{conn: conn} do
2537 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2539 assert json_response(conn, 404) == %{"error" => "Record not found"}
2542 test "returns 403 error when user has hidden own favorites", %{
2544 current_user: current_user
2546 user = insert(:user, %{info: %{hide_favorites: true}})
2547 activity = insert(:note_activity)
2548 CommonAPI.favorite(activity.id, user)
2552 |> assign(:user, current_user)
2553 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2555 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2558 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2559 user = insert(:user)
2560 activity = insert(:note_activity)
2561 CommonAPI.favorite(activity.id, user)
2565 |> assign(:user, current_user)
2566 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2568 assert user.info.hide_favorites
2569 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2573 test "get instance information", %{conn: conn} do
2574 conn = get(conn, "/api/v1/instance")
2575 assert result = json_response(conn, 200)
2577 email = Config.get([:instance, :email])
2578 # Note: not checking for "max_toot_chars" since it's optional
2584 "email" => from_config_email,
2586 "streaming_api" => _
2591 "registrations" => _,
2595 assert email == from_config_email
2598 test "get instance stats", %{conn: conn} do
2599 user = insert(:user, %{local: true})
2601 user2 = insert(:user, %{local: true})
2602 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2604 insert(:user, %{local: false, nickname: "u@peer1.com"})
2605 insert(:user, %{local: false, nickname: "u@peer2.com"})
2607 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2609 # Stats should count users with missing or nil `info.deactivated` value
2610 user = User.get_cached_by_id(user.id)
2611 info_change = Changeset.change(user.info, %{deactivated: nil})
2615 |> Changeset.change()
2616 |> Changeset.put_embed(:info, info_change)
2617 |> User.update_and_set_cache()
2619 Pleroma.Stats.force_update()
2621 conn = get(conn, "/api/v1/instance")
2623 assert result = json_response(conn, 200)
2625 stats = result["stats"]
2628 assert stats["user_count"] == 1
2629 assert stats["status_count"] == 1
2630 assert stats["domain_count"] == 2
2633 test "get peers", %{conn: conn} do
2634 insert(:user, %{local: false, nickname: "u@peer1.com"})
2635 insert(:user, %{local: false, nickname: "u@peer2.com"})
2637 Pleroma.Stats.force_update()
2639 conn = get(conn, "/api/v1/instance/peers")
2641 assert result = json_response(conn, 200)
2643 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2646 test "put settings", %{conn: conn} do
2647 user = insert(:user)
2651 |> assign(:user, user)
2652 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2654 assert _result = json_response(conn, 200)
2656 user = User.get_cached_by_ap_id(user.ap_id)
2657 assert user.info.settings == %{"programming" => "socks"}
2660 describe "pinned statuses" do
2662 user = insert(:user)
2663 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2665 [user: user, activity: activity]
2668 clear_config([:instance, :max_pinned_statuses]) do
2669 Config.put([:instance, :max_pinned_statuses], 1)
2672 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2673 {:ok, _} = CommonAPI.pin(activity.id, user)
2677 |> assign(:user, user)
2678 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2679 |> json_response(200)
2681 id_str = to_string(activity.id)
2683 assert [%{"id" => ^id_str, "pinned" => true}] = result
2686 test "pin status", %{conn: conn, user: user, activity: activity} do
2687 id_str = to_string(activity.id)
2689 assert %{"id" => ^id_str, "pinned" => true} =
2691 |> assign(:user, user)
2692 |> post("/api/v1/statuses/#{activity.id}/pin")
2693 |> json_response(200)
2695 assert [%{"id" => ^id_str, "pinned" => true}] =
2697 |> assign(:user, user)
2698 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2699 |> json_response(200)
2702 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2703 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2707 |> assign(:user, user)
2708 |> post("/api/v1/statuses/#{dm.id}/pin")
2710 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2713 test "unpin status", %{conn: conn, user: user, activity: activity} do
2714 {:ok, _} = CommonAPI.pin(activity.id, user)
2716 id_str = to_string(activity.id)
2717 user = refresh_record(user)
2719 assert %{"id" => ^id_str, "pinned" => false} =
2721 |> assign(:user, user)
2722 |> post("/api/v1/statuses/#{activity.id}/unpin")
2723 |> json_response(200)
2727 |> assign(:user, user)
2728 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2729 |> json_response(200)
2732 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2735 |> assign(:user, user)
2736 |> post("/api/v1/statuses/1/unpin")
2738 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2741 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2742 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2744 id_str_one = to_string(activity_one.id)
2746 assert %{"id" => ^id_str_one, "pinned" => true} =
2748 |> assign(:user, user)
2749 |> post("/api/v1/statuses/#{id_str_one}/pin")
2750 |> json_response(200)
2752 user = refresh_record(user)
2754 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2756 |> assign(:user, user)
2757 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2758 |> json_response(400)
2764 Config.put([:rich_media, :enabled], true)
2766 user = insert(:user)
2770 test "returns rich-media card", %{conn: conn, user: user} do
2771 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2774 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2775 "provider_name" => "example.com",
2776 "provider_url" => "https://example.com",
2777 "title" => "The Rock",
2779 "url" => "https://example.com/ogp",
2781 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2784 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2785 "title" => "The Rock",
2786 "type" => "video.movie",
2787 "url" => "https://example.com/ogp",
2789 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2796 |> get("/api/v1/statuses/#{activity.id}/card")
2797 |> json_response(200)
2799 assert response == card_data
2801 # works with private posts
2803 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2807 |> assign(:user, user)
2808 |> get("/api/v1/statuses/#{activity.id}/card")
2809 |> json_response(200)
2811 assert response_two == card_data
2814 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2816 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2820 |> get("/api/v1/statuses/#{activity.id}/card")
2821 |> json_response(:ok)
2823 assert response == %{
2825 "title" => "Pleroma",
2826 "description" => "",
2828 "provider_name" => "example.com",
2829 "provider_url" => "https://example.com",
2830 "url" => "https://example.com/ogp-missing-data",
2833 "title" => "Pleroma",
2834 "type" => "website",
2835 "url" => "https://example.com/ogp-missing-data"
2843 user = insert(:user)
2844 for_user = insert(:user)
2847 CommonAPI.post(user, %{
2848 "status" => "heweoo?"
2852 CommonAPI.post(user, %{
2853 "status" => "heweoo!"
2858 |> assign(:user, for_user)
2859 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2861 assert json_response(response1, 200)["bookmarked"] == true
2865 |> assign(:user, for_user)
2866 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2868 assert json_response(response2, 200)["bookmarked"] == true
2872 |> assign(:user, for_user)
2873 |> get("/api/v1/bookmarks")
2875 assert [json_response(response2, 200), json_response(response1, 200)] ==
2876 json_response(bookmarks, 200)
2880 |> assign(:user, for_user)
2881 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2883 assert json_response(response1, 200)["bookmarked"] == false
2887 |> assign(:user, for_user)
2888 |> get("/api/v1/bookmarks")
2890 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2893 describe "conversation muting" do
2895 post_user = insert(:user)
2896 user = insert(:user)
2898 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2900 [user: user, activity: activity]
2903 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2904 id_str = to_string(activity.id)
2906 assert %{"id" => ^id_str, "muted" => true} =
2908 |> assign(:user, user)
2909 |> post("/api/v1/statuses/#{activity.id}/mute")
2910 |> json_response(200)
2913 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2914 {:ok, _} = CommonAPI.add_mute(user, activity)
2918 |> assign(:user, user)
2919 |> post("/api/v1/statuses/#{activity.id}/mute")
2921 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2924 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2925 {:ok, _} = CommonAPI.add_mute(user, activity)
2927 id_str = to_string(activity.id)
2928 user = refresh_record(user)
2930 assert %{"id" => ^id_str, "muted" => false} =
2932 |> assign(:user, user)
2933 |> post("/api/v1/statuses/#{activity.id}/unmute")
2934 |> json_response(200)
2938 describe "reports" do
2940 reporter = insert(:user)
2941 target_user = insert(:user)
2943 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2945 [reporter: reporter, target_user: target_user, activity: activity]
2948 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2949 assert %{"action_taken" => false, "id" => _} =
2951 |> assign(:user, reporter)
2952 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2953 |> json_response(200)
2956 test "submit a report with statuses and comment", %{
2959 target_user: target_user,
2962 assert %{"action_taken" => false, "id" => _} =
2964 |> assign(:user, reporter)
2965 |> post("/api/v1/reports", %{
2966 "account_id" => target_user.id,
2967 "status_ids" => [activity.id],
2968 "comment" => "bad status!",
2969 "forward" => "false"
2971 |> json_response(200)
2974 test "account_id is required", %{
2979 assert %{"error" => "Valid `account_id` required"} =
2981 |> assign(:user, reporter)
2982 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2983 |> json_response(400)
2986 test "comment must be up to the size specified in the config", %{
2989 target_user: target_user
2991 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2992 comment = String.pad_trailing("a", max_size + 1, "a")
2994 error = %{"error" => "Comment must be up to #{max_size} characters"}
2998 |> assign(:user, reporter)
2999 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3000 |> json_response(400)
3003 test "returns error when account is not exist", %{
3010 |> assign(:user, reporter)
3011 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3013 assert json_response(conn, 400) == %{"error" => "Account not found"}
3017 describe "link headers" do
3018 test "preserves parameters in link headers", %{conn: conn} do
3019 user = insert(:user)
3020 other_user = insert(:user)
3023 CommonAPI.post(other_user, %{
3024 "status" => "hi @#{user.nickname}",
3025 "visibility" => "public"
3029 CommonAPI.post(other_user, %{
3030 "status" => "hi @#{user.nickname}",
3031 "visibility" => "public"
3034 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3035 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3039 |> assign(:user, user)
3040 |> get("/api/v1/notifications", %{media_only: true})
3042 assert [link_header] = get_resp_header(conn, "link")
3043 assert link_header =~ ~r/media_only=true/
3044 assert link_header =~ ~r/min_id=#{notification2.id}/
3045 assert link_header =~ ~r/max_id=#{notification1.id}/
3049 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3050 # Need to set an old-style integer ID to reproduce the problem
3051 # (these are no longer assigned to new accounts but were preserved
3052 # for existing accounts during the migration to flakeIDs)
3053 user_one = insert(:user, %{id: 1212})
3054 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3058 |> get("/api/v1/accounts/#{user_one.id}")
3062 |> get("/api/v1/accounts/#{user_two.nickname}")
3066 |> get("/api/v1/accounts/#{user_two.id}")
3068 acc_one = json_response(resp_one, 200)
3069 acc_two = json_response(resp_two, 200)
3070 acc_three = json_response(resp_three, 200)
3071 refute acc_one == acc_two
3072 assert acc_two == acc_three
3075 describe "custom emoji" do
3076 test "with tags", %{conn: conn} do
3079 |> get("/api/v1/custom_emojis")
3080 |> json_response(200)
3082 assert Map.has_key?(emoji, "shortcode")
3083 assert Map.has_key?(emoji, "static_url")
3084 assert Map.has_key?(emoji, "tags")
3085 assert is_list(emoji["tags"])
3086 assert Map.has_key?(emoji, "category")
3087 assert Map.has_key?(emoji, "url")
3088 assert Map.has_key?(emoji, "visible_in_picker")
3092 describe "index/2 redirections" do
3093 setup %{conn: conn} do
3097 signing_salt: "cooldude"
3102 |> Plug.Session.call(Plug.Session.init(session_opts))
3105 test_path = "/web/statuses/test"
3106 %{conn: conn, path: test_path}
3109 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3110 conn = get(conn, path)
3112 assert conn.status == 302
3113 assert redirected_to(conn) == "/web/login"
3116 test "redirects not logged-in users to the login page on private instances", %{
3120 Config.put([:instance, :public], false)
3122 conn = get(conn, path)
3124 assert conn.status == 302
3125 assert redirected_to(conn) == "/web/login"
3128 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3129 token = insert(:oauth_token)
3133 |> assign(:user, token.user)
3134 |> put_session(:oauth_token, token.token)
3137 assert conn.status == 200
3140 test "saves referer path to session", %{conn: conn, path: path} do
3141 conn = get(conn, path)
3142 return_to = Plug.Conn.get_session(conn, :return_to)
3144 assert return_to == path
3147 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3148 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3149 auth = insert(:oauth_authorization, app: app)
3153 |> put_session(:return_to, path)
3154 |> get("/web/login", %{code: auth.token})
3156 assert conn.status == 302
3157 assert redirected_to(conn) == path
3160 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3161 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3162 auth = insert(:oauth_authorization, app: app)
3164 conn = get(conn, "/web/login", %{code: auth.token})
3166 assert conn.status == 302
3167 assert redirected_to(conn) == "/web/getting-started"
3171 describe "scheduled activities" do
3172 test "creates a scheduled activity", %{conn: conn} do
3173 user = insert(:user)
3174 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3178 |> assign(:user, user)
3179 |> post("/api/v1/statuses", %{
3180 "status" => "scheduled",
3181 "scheduled_at" => scheduled_at
3184 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3185 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3186 assert [] == Repo.all(Activity)
3189 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3190 user = insert(:user)
3191 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3193 file = %Plug.Upload{
3194 content_type: "image/jpg",
3195 path: Path.absname("test/fixtures/image.jpg"),
3196 filename: "an_image.jpg"
3199 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3203 |> assign(:user, user)
3204 |> post("/api/v1/statuses", %{
3205 "media_ids" => [to_string(upload.id)],
3206 "status" => "scheduled",
3207 "scheduled_at" => scheduled_at
3210 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3211 assert %{"type" => "image"} = media_attachment
3214 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3216 user = insert(:user)
3219 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3223 |> assign(:user, user)
3224 |> post("/api/v1/statuses", %{
3225 "status" => "not scheduled",
3226 "scheduled_at" => scheduled_at
3229 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3230 assert [] == Repo.all(ScheduledActivity)
3233 test "returns error when daily user limit is exceeded", %{conn: conn} do
3234 user = insert(:user)
3237 NaiveDateTime.utc_now()
3238 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3239 |> NaiveDateTime.to_iso8601()
3241 attrs = %{params: %{}, scheduled_at: today}
3242 {:ok, _} = ScheduledActivity.create(user, attrs)
3243 {:ok, _} = ScheduledActivity.create(user, attrs)
3247 |> assign(:user, user)
3248 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3250 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3253 test "returns error when total user limit is exceeded", %{conn: conn} do
3254 user = insert(:user)
3257 NaiveDateTime.utc_now()
3258 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3259 |> NaiveDateTime.to_iso8601()
3262 NaiveDateTime.utc_now()
3263 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3264 |> NaiveDateTime.to_iso8601()
3266 attrs = %{params: %{}, scheduled_at: today}
3267 {:ok, _} = ScheduledActivity.create(user, attrs)
3268 {:ok, _} = ScheduledActivity.create(user, attrs)
3269 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3273 |> assign(:user, user)
3274 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3276 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3279 test "shows scheduled activities", %{conn: conn} do
3280 user = insert(:user)
3281 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3282 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3283 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3284 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3288 |> assign(:user, user)
3293 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3295 result = json_response(conn_res, 200)
3296 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3301 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3303 result = json_response(conn_res, 200)
3304 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3309 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3311 result = json_response(conn_res, 200)
3312 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3315 test "shows a scheduled activity", %{conn: conn} do
3316 user = insert(:user)
3317 scheduled_activity = insert(:scheduled_activity, user: user)
3321 |> assign(:user, user)
3322 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3324 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3325 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3329 |> assign(:user, user)
3330 |> get("/api/v1/scheduled_statuses/404")
3332 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3335 test "updates a scheduled activity", %{conn: conn} do
3336 user = insert(:user)
3337 scheduled_activity = insert(:scheduled_activity, user: user)
3340 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3344 |> assign(:user, user)
3345 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3346 scheduled_at: new_scheduled_at
3349 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3350 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3354 |> assign(:user, user)
3355 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3357 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3360 test "deletes a scheduled activity", %{conn: conn} do
3361 user = insert(:user)
3362 scheduled_activity = insert(:scheduled_activity, user: user)
3366 |> assign(:user, user)
3367 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3369 assert %{} = json_response(res_conn, 200)
3370 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3374 |> assign(:user, user)
3375 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3377 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3381 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3382 user1 = insert(:user)
3383 user2 = insert(:user)
3384 user3 = insert(:user)
3386 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3388 # Reply to status from another user
3391 |> assign(:user, user2)
3392 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3394 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3396 activity = Activity.get_by_id_with_object(id)
3398 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3399 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3401 # Reblog from the third user
3404 |> assign(:user, user3)
3405 |> post("/api/v1/statuses/#{activity.id}/reblog")
3407 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3408 json_response(conn2, 200)
3410 assert to_string(activity.id) == id
3412 # Getting third user status
3415 |> assign(:user, user3)
3416 |> get("api/v1/timelines/home")
3418 [reblogged_activity] = json_response(conn3, 200)
3420 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3422 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3423 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3426 describe "create account by app" do
3427 test "Account registration via Application", %{conn: conn} do
3430 |> post("/api/v1/apps", %{
3431 client_name: "client_name",
3432 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3433 scopes: "read, write, follow"
3437 "client_id" => client_id,
3438 "client_secret" => client_secret,
3440 "name" => "client_name",
3441 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3444 } = json_response(conn, 200)
3448 |> post("/oauth/token", %{
3449 grant_type: "client_credentials",
3450 client_id: client_id,
3451 client_secret: client_secret
3454 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3455 json_response(conn, 200)
3458 token_from_db = Repo.get_by(Token, token: token)
3459 assert token_from_db
3461 assert scope == "read write follow"
3465 |> put_req_header("authorization", "Bearer " <> token)
3466 |> post("/api/v1/accounts", %{
3468 email: "lain@example.org",
3469 password: "PlzDontHackLain",
3474 "access_token" => token,
3475 "created_at" => _created_at,
3477 "token_type" => "Bearer"
3478 } = json_response(conn, 200)
3480 token_from_db = Repo.get_by(Token, token: token)
3481 assert token_from_db
3482 token_from_db = Repo.preload(token_from_db, :user)
3483 assert token_from_db.user
3485 assert token_from_db.user.info.confirmation_pending
3488 test "rate limit", %{conn: conn} do
3489 app_token = insert(:oauth_token, user: nil)
3492 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3493 |> Map.put(:remote_ip, {15, 15, 15, 15})
3498 |> post("/api/v1/accounts", %{
3499 username: "#{i}lain",
3500 email: "#{i}lain@example.org",
3501 password: "PlzDontHackLain",
3506 "access_token" => token,
3507 "created_at" => _created_at,
3509 "token_type" => "Bearer"
3510 } = json_response(conn, 200)
3512 token_from_db = Repo.get_by(Token, token: token)
3513 assert token_from_db
3514 token_from_db = Repo.preload(token_from_db, :user)
3515 assert token_from_db.user
3517 assert token_from_db.user.info.confirmation_pending
3522 |> post("/api/v1/accounts", %{
3524 email: "6lain@example.org",
3525 password: "PlzDontHackLain",
3529 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3533 describe "GET /api/v1/polls/:id" do
3534 test "returns poll entity for object id", %{conn: conn} do
3535 user = insert(:user)
3538 CommonAPI.post(user, %{
3539 "status" => "Pleroma does",
3540 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3543 object = Object.normalize(activity)
3547 |> assign(:user, user)
3548 |> get("/api/v1/polls/#{object.id}")
3550 response = json_response(conn, 200)
3551 id = to_string(object.id)
3552 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3555 test "does not expose polls for private statuses", %{conn: conn} do
3556 user = insert(:user)
3557 other_user = insert(:user)
3560 CommonAPI.post(user, %{
3561 "status" => "Pleroma does",
3562 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3563 "visibility" => "private"
3566 object = Object.normalize(activity)
3570 |> assign(:user, other_user)
3571 |> get("/api/v1/polls/#{object.id}")
3573 assert json_response(conn, 404)
3577 describe "POST /api/v1/polls/:id/votes" do
3578 test "votes are added to the poll", %{conn: conn} do
3579 user = insert(:user)
3580 other_user = insert(:user)
3583 CommonAPI.post(user, %{
3584 "status" => "A very delicious sandwich",
3586 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3592 object = Object.normalize(activity)
3596 |> assign(:user, other_user)
3597 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3599 assert json_response(conn, 200)
3600 object = Object.get_by_id(object.id)
3602 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3607 test "author can't vote", %{conn: conn} do
3608 user = insert(:user)
3611 CommonAPI.post(user, %{
3612 "status" => "Am I cute?",
3613 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3616 object = Object.normalize(activity)
3619 |> assign(:user, user)
3620 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3621 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3623 object = Object.get_by_id(object.id)
3625 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3628 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3629 user = insert(:user)
3630 other_user = insert(:user)
3633 CommonAPI.post(user, %{
3634 "status" => "The glass is",
3635 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3638 object = Object.normalize(activity)
3641 |> assign(:user, other_user)
3642 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3643 |> json_response(422) == %{"error" => "Too many choices"}
3645 object = Object.get_by_id(object.id)
3647 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3652 test "does not allow choice index to be greater than options count", %{conn: conn} do
3653 user = insert(:user)
3654 other_user = insert(:user)
3657 CommonAPI.post(user, %{
3658 "status" => "Am I cute?",
3659 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3662 object = Object.normalize(activity)
3666 |> assign(:user, other_user)
3667 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3669 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3672 test "returns 404 error when object is not exist", %{conn: conn} do
3673 user = insert(:user)
3677 |> assign(:user, user)
3678 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3680 assert json_response(conn, 404) == %{"error" => "Record not found"}
3683 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3684 user = insert(:user)
3685 other_user = insert(:user)
3688 CommonAPI.post(user, %{
3689 "status" => "Am I cute?",
3690 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3691 "visibility" => "private"
3694 object = Object.normalize(activity)
3698 |> assign(:user, other_user)
3699 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3701 assert json_response(conn, 404) == %{"error" => "Record not found"}
3705 describe "GET /api/v1/statuses/:id/favourited_by" do
3707 user = insert(:user)
3708 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3712 |> assign(:user, user)
3714 [conn: conn, activity: activity]
3717 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3718 other_user = insert(:user)
3719 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3723 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3724 |> json_response(:ok)
3726 [%{"id" => id}] = response
3728 assert id == other_user.id
3731 test "returns empty array when status has not been favorited yet", %{
3737 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3738 |> json_response(:ok)
3740 assert Enum.empty?(response)
3743 test "does not return users who have favorited the status but are blocked", %{
3744 conn: %{assigns: %{user: user}} = conn,
3747 other_user = insert(:user)
3748 {:ok, user} = User.block(user, other_user)
3750 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3754 |> assign(:user, user)
3755 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3756 |> json_response(:ok)
3758 assert Enum.empty?(response)
3761 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3762 other_user = insert(:user)
3763 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3767 |> assign(:user, nil)
3768 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3769 |> json_response(:ok)
3771 [%{"id" => id}] = response
3772 assert id == other_user.id
3776 describe "GET /api/v1/statuses/:id/reblogged_by" do
3778 user = insert(:user)
3779 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3783 |> assign(:user, user)
3785 [conn: conn, activity: activity]
3788 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3789 other_user = insert(:user)
3790 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3794 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3795 |> json_response(:ok)
3797 [%{"id" => id}] = response
3799 assert id == other_user.id
3802 test "returns empty array when status has not been reblogged yet", %{
3808 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3809 |> json_response(:ok)
3811 assert Enum.empty?(response)
3814 test "does not return users who have reblogged the status but are blocked", %{
3815 conn: %{assigns: %{user: user}} = conn,
3818 other_user = insert(:user)
3819 {:ok, user} = User.block(user, other_user)
3821 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3825 |> assign(:user, user)
3826 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3827 |> json_response(:ok)
3829 assert Enum.empty?(response)
3832 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3833 other_user = insert(:user)
3834 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3838 |> assign(:user, nil)
3839 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3840 |> json_response(:ok)
3842 [%{"id" => id}] = response
3843 assert id == other_user.id
3847 describe "POST /auth/password, with valid parameters" do
3848 setup %{conn: conn} do
3849 user = insert(:user)
3850 conn = post(conn, "/auth/password?email=#{user.email}")
3851 %{conn: conn, user: user}
3854 test "it returns 204", %{conn: conn} do
3855 assert json_response(conn, :no_content)
3858 test "it creates a PasswordResetToken record for user", %{user: user} do
3859 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3863 test "it sends an email to user", %{user: user} do
3864 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3866 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3867 notify_email = Config.get([:instance, :notify_email])
3868 instance_name = Config.get([:instance, :name])
3871 from: {instance_name, notify_email},
3872 to: {user.name, user.email},
3873 html_body: email.html_body
3878 describe "POST /auth/password, with invalid parameters" do
3880 user = insert(:user)
3884 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3885 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3886 assert conn.status == 404
3887 assert conn.resp_body == ""
3890 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3891 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3892 conn = post(conn, "/auth/password?email=#{user.email}")
3893 assert conn.status == 400
3894 assert conn.resp_body == ""
3898 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3900 user = insert(:user)
3901 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3905 |> Changeset.change()
3906 |> Changeset.put_embed(:info, info_change)
3909 assert user.info.confirmation_pending
3914 clear_config([:instance, :account_activation_required]) do
3915 Config.put([:instance, :account_activation_required], true)
3918 test "resend account confirmation email", %{conn: conn, user: user} do
3920 |> assign(:user, user)
3921 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3922 |> json_response(:no_content)
3924 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3925 notify_email = Config.get([:instance, :notify_email])
3926 instance_name = Config.get([:instance, :name])
3929 from: {instance_name, notify_email},
3930 to: {user.name, user.email},
3931 html_body: email.html_body
3936 describe "GET /api/v1/suggestions" do
3938 user = insert(:user)
3939 other_user = insert(:user)
3940 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3941 url500 = "http://test500?#{host}&#{user.nickname}"
3942 url200 = "http://test200?#{host}&#{user.nickname}"
3945 %{method: :get, url: ^url500} ->
3946 %Tesla.Env{status: 500, body: "bad request"}
3948 %{method: :get, url: ^url200} ->
3952 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3954 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3958 [user: user, other_user: other_user]
3961 clear_config(:suggestions)
3963 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3964 Config.put([:suggestions, :enabled], false)
3968 |> assign(:user, user)
3969 |> get("/api/v1/suggestions")
3970 |> json_response(200)
3975 test "returns error", %{conn: conn, user: user} do
3976 Config.put([:suggestions, :enabled], true)
3977 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
3981 |> assign(:user, user)
3982 |> get("/api/v1/suggestions")
3983 |> json_response(500)
3985 assert res == "Something went wrong"
3988 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
3989 Config.put([:suggestions, :enabled], true)
3990 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
3994 |> assign(:user, user)
3995 |> get("/api/v1/suggestions")
3996 |> json_response(200)
4001 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4002 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4006 "acct" => other_user.ap_id,
4007 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4008 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4009 "id" => other_user.id