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.AccountControllerTest do
6 use Pleroma.Web.ConnCase
9 alias Pleroma.Web.ActivityPub.ActivityPub
10 alias Pleroma.Web.CommonAPI
12 import Pleroma.Factory
14 describe "account fetching" do
20 |> get("/api/v1/accounts/#{user.id}")
22 assert %{"id" => id} = json_response(conn, 200)
23 assert id == to_string(user.id)
27 |> get("/api/v1/accounts/-1")
29 assert %{"error" => "Can't find user"} = json_response(conn, 404)
32 test "works by nickname" do
37 |> get("/api/v1/accounts/#{user.nickname}")
39 assert %{"id" => id} = json_response(conn, 200)
43 test "works by nickname for remote users" do
44 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
45 Pleroma.Config.put([:instance, :limit_to_local_content], false)
46 user = insert(:user, nickname: "user@example.com", local: false)
50 |> get("/api/v1/accounts/#{user.nickname}")
52 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
53 assert %{"id" => id} = json_response(conn, 200)
57 test "respects limit_to_local_content == :all for remote user nicknames" do
58 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
59 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
61 user = insert(:user, nickname: "user@example.com", local: false)
65 |> get("/api/v1/accounts/#{user.nickname}")
67 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
68 assert json_response(conn, 404)
71 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
72 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
73 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
75 user = insert(:user, nickname: "user@example.com", local: false)
76 reading_user = insert(:user)
80 |> get("/api/v1/accounts/#{user.nickname}")
82 assert json_response(conn, 404)
86 |> assign(:user, reading_user)
87 |> get("/api/v1/accounts/#{user.nickname}")
89 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
90 assert %{"id" => id} = json_response(conn, 200)
94 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
95 # Need to set an old-style integer ID to reproduce the problem
96 # (these are no longer assigned to new accounts but were preserved
97 # for existing accounts during the migration to flakeIDs)
98 user_one = insert(:user, %{id: 1212})
99 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
103 |> get("/api/v1/accounts/#{user_one.id}")
107 |> get("/api/v1/accounts/#{user_two.nickname}")
111 |> get("/api/v1/accounts/#{user_two.id}")
113 acc_one = json_response(resp_one, 200)
114 acc_two = json_response(resp_two, 200)
115 acc_three = json_response(resp_three, 200)
116 refute acc_one == acc_two
117 assert acc_two == acc_three
121 describe "user timelines" do
122 test "gets a users statuses", %{conn: conn} do
123 user_one = insert(:user)
124 user_two = insert(:user)
125 user_three = insert(:user)
127 {:ok, user_three} = User.follow(user_three, user_one)
129 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
131 {:ok, direct_activity} =
132 CommonAPI.post(user_one, %{
133 "status" => "Hi, @#{user_two.nickname}.",
134 "visibility" => "direct"
137 {:ok, private_activity} =
138 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
142 |> get("/api/v1/accounts/#{user_one.id}/statuses")
144 assert [%{"id" => id}] = json_response(resp, 200)
145 assert id == to_string(activity.id)
149 |> assign(:user, user_two)
150 |> get("/api/v1/accounts/#{user_one.id}/statuses")
152 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
153 assert id_one == to_string(direct_activity.id)
154 assert id_two == to_string(activity.id)
158 |> assign(:user, user_three)
159 |> get("/api/v1/accounts/#{user_one.id}/statuses")
161 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
162 assert id_one == to_string(private_activity.id)
163 assert id_two == to_string(activity.id)
166 test "unimplemented pinned statuses feature", %{conn: conn} do
167 note = insert(:note_activity)
168 user = User.get_cached_by_ap_id(note.data["actor"])
172 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
174 assert json_response(conn, 200) == []
177 test "gets an users media", %{conn: conn} do
178 note = insert(:note_activity)
179 user = User.get_cached_by_ap_id(note.data["actor"])
182 content_type: "image/jpg",
183 path: Path.absname("test/fixtures/image.jpg"),
184 filename: "an_image.jpg"
187 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
189 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
193 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
195 assert [%{"id" => id}] = json_response(conn, 200)
196 assert id == to_string(image_post.id)
200 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
202 assert [%{"id" => id}] = json_response(conn, 200)
203 assert id == to_string(image_post.id)
206 test "gets a user's statuses without reblogs", %{conn: conn} do
208 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
209 {:ok, _, _} = CommonAPI.repeat(post.id, user)
213 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
215 assert [%{"id" => id}] = json_response(conn, 200)
216 assert id == to_string(post.id)
220 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
222 assert [%{"id" => id}] = json_response(conn, 200)
223 assert id == to_string(post.id)
226 test "filters user's statuses by a hashtag", %{conn: conn} do
228 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
229 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
233 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
235 assert [%{"id" => id}] = json_response(conn, 200)
236 assert id == to_string(post.id)
240 describe "followers" do
241 test "getting followers", %{conn: conn} do
243 other_user = insert(:user)
244 {:ok, user} = User.follow(user, other_user)
248 |> get("/api/v1/accounts/#{other_user.id}/followers")
250 assert [%{"id" => id}] = json_response(conn, 200)
251 assert id == to_string(user.id)
254 test "getting followers, hide_followers", %{conn: conn} do
256 other_user = insert(:user, %{info: %{hide_followers: true}})
257 {:ok, _user} = User.follow(user, other_user)
261 |> get("/api/v1/accounts/#{other_user.id}/followers")
263 assert [] == json_response(conn, 200)
266 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
268 other_user = insert(:user, %{info: %{hide_followers: true}})
269 {:ok, _user} = User.follow(user, other_user)
273 |> assign(:user, other_user)
274 |> get("/api/v1/accounts/#{other_user.id}/followers")
276 refute [] == json_response(conn, 200)
279 test "getting followers, pagination", %{conn: conn} do
281 follower1 = insert(:user)
282 follower2 = insert(:user)
283 follower3 = insert(:user)
284 {:ok, _} = User.follow(follower1, user)
285 {:ok, _} = User.follow(follower2, user)
286 {:ok, _} = User.follow(follower3, user)
290 |> assign(:user, user)
294 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
296 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
297 assert id3 == follower3.id
298 assert id2 == follower2.id
302 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
304 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
305 assert id2 == follower2.id
306 assert id1 == follower1.id
310 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
312 assert [%{"id" => id2}] = json_response(res_conn, 200)
313 assert id2 == follower2.id
315 assert [link_header] = get_resp_header(res_conn, "link")
316 assert link_header =~ ~r/min_id=#{follower2.id}/
317 assert link_header =~ ~r/max_id=#{follower2.id}/
321 describe "following" do
322 test "getting following", %{conn: conn} do
324 other_user = insert(:user)
325 {:ok, user} = User.follow(user, other_user)
329 |> get("/api/v1/accounts/#{user.id}/following")
331 assert [%{"id" => id}] = json_response(conn, 200)
332 assert id == to_string(other_user.id)
335 test "getting following, hide_follows", %{conn: conn} do
336 user = insert(:user, %{info: %{hide_follows: true}})
337 other_user = insert(:user)
338 {:ok, user} = User.follow(user, other_user)
342 |> get("/api/v1/accounts/#{user.id}/following")
344 assert [] == json_response(conn, 200)
347 test "getting following, hide_follows, same user requesting", %{conn: conn} do
348 user = insert(:user, %{info: %{hide_follows: true}})
349 other_user = insert(:user)
350 {:ok, user} = User.follow(user, other_user)
354 |> assign(:user, user)
355 |> get("/api/v1/accounts/#{user.id}/following")
357 refute [] == json_response(conn, 200)
360 test "getting following, pagination", %{conn: conn} do
362 following1 = insert(:user)
363 following2 = insert(:user)
364 following3 = insert(:user)
365 {:ok, _} = User.follow(user, following1)
366 {:ok, _} = User.follow(user, following2)
367 {:ok, _} = User.follow(user, following3)
371 |> assign(:user, user)
375 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
377 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
378 assert id3 == following3.id
379 assert id2 == following2.id
383 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
385 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
386 assert id2 == following2.id
387 assert id1 == following1.id
391 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
393 assert [%{"id" => id2}] = json_response(res_conn, 200)
394 assert id2 == following2.id
396 assert [link_header] = get_resp_header(res_conn, "link")
397 assert link_header =~ ~r/min_id=#{following2.id}/
398 assert link_header =~ ~r/max_id=#{following2.id}/
402 describe "follow/unfollow" do
403 test "following / unfollowing a user", %{conn: conn} do
405 other_user = insert(:user)
409 |> assign(:user, user)
410 |> post("/api/v1/accounts/#{other_user.id}/follow")
412 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
414 user = User.get_cached_by_id(user.id)
418 |> assign(:user, user)
419 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
421 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
423 user = User.get_cached_by_id(user.id)
427 |> assign(:user, user)
428 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
430 assert %{"id" => id} = json_response(conn, 200)
431 assert id == to_string(other_user.id)
434 test "following without reblogs" do
435 follower = insert(:user)
436 followed = insert(:user)
437 other_user = insert(:user)
441 |> assign(:user, follower)
442 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
444 assert %{"showing_reblogs" => false} = json_response(conn, 200)
446 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
447 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
451 |> assign(:user, User.get_cached_by_id(follower.id))
452 |> get("/api/v1/timelines/home")
454 assert [] == json_response(conn, 200)
458 |> assign(:user, follower)
459 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
461 assert %{"showing_reblogs" => true} = json_response(conn, 200)
465 |> assign(:user, User.get_cached_by_id(follower.id))
466 |> get("/api/v1/timelines/home")
468 expected_activity_id = reblog.id
469 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
472 test "following / unfollowing errors" do
477 |> assign(:user, user)
480 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
481 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
484 user = User.get_cached_by_id(user.id)
485 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
486 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
488 # self follow via uri
489 user = User.get_cached_by_id(user.id)
490 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
491 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
493 # follow non existing user
494 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
495 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
497 # follow non existing user via uri
498 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
499 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
501 # unfollow non existing user
502 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
503 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
507 describe "mute/unmute" do
508 test "with notifications", %{conn: conn} do
510 other_user = insert(:user)
514 |> assign(:user, user)
515 |> post("/api/v1/accounts/#{other_user.id}/mute")
517 response = json_response(conn, 200)
519 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
520 user = User.get_cached_by_id(user.id)
524 |> assign(:user, user)
525 |> post("/api/v1/accounts/#{other_user.id}/unmute")
527 response = json_response(conn, 200)
528 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
531 test "without notifications", %{conn: conn} do
533 other_user = insert(:user)
537 |> assign(:user, user)
538 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
540 response = json_response(conn, 200)
542 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
543 user = User.get_cached_by_id(user.id)
547 |> assign(:user, user)
548 |> post("/api/v1/accounts/#{other_user.id}/unmute")
550 response = json_response(conn, 200)
551 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
555 describe "getting favorites timeline of specified user" do
557 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
558 [current_user: current_user, user: user]
561 test "returns list of statuses favorited by specified user", %{
563 current_user: current_user,
566 [activity | _] = insert_pair(:note_activity)
567 CommonAPI.favorite(activity.id, user)
571 |> assign(:user, current_user)
572 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
573 |> json_response(:ok)
577 assert length(response) == 1
578 assert like["id"] == activity.id
581 test "returns favorites for specified user_id when user is not logged in", %{
585 activity = insert(:note_activity)
586 CommonAPI.favorite(activity.id, user)
590 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
591 |> json_response(:ok)
593 assert length(response) == 1
596 test "returns favorited DM only when user is logged in and he is one of recipients", %{
598 current_user: current_user,
602 CommonAPI.post(current_user, %{
603 "status" => "Hi @#{user.nickname}!",
604 "visibility" => "direct"
607 CommonAPI.favorite(direct.id, user)
611 |> assign(:user, current_user)
612 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
613 |> json_response(:ok)
615 assert length(response) == 1
619 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
620 |> json_response(:ok)
622 assert Enum.empty?(anonymous_response)
625 test "does not return others' favorited DM when user is not one of recipients", %{
627 current_user: current_user,
630 user_two = insert(:user)
633 CommonAPI.post(user_two, %{
634 "status" => "Hi @#{user.nickname}!",
635 "visibility" => "direct"
638 CommonAPI.favorite(direct.id, user)
642 |> assign(:user, current_user)
643 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
644 |> json_response(:ok)
646 assert Enum.empty?(response)
649 test "paginates favorites using since_id and max_id", %{
651 current_user: current_user,
654 activities = insert_list(10, :note_activity)
656 Enum.each(activities, fn activity ->
657 CommonAPI.favorite(activity.id, user)
660 third_activity = Enum.at(activities, 2)
661 seventh_activity = Enum.at(activities, 6)
665 |> assign(:user, current_user)
666 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
667 since_id: third_activity.id,
668 max_id: seventh_activity.id
670 |> json_response(:ok)
672 assert length(response) == 3
673 refute third_activity in response
674 refute seventh_activity in response
677 test "limits favorites using limit parameter", %{
679 current_user: current_user,
683 |> insert_list(:note_activity)
684 |> Enum.each(fn activity ->
685 CommonAPI.favorite(activity.id, user)
690 |> assign(:user, current_user)
691 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
692 |> json_response(:ok)
694 assert length(response) == 3
697 test "returns empty response when user does not have any favorited statuses", %{
699 current_user: current_user,
704 |> assign(:user, current_user)
705 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
706 |> json_response(:ok)
708 assert Enum.empty?(response)
711 test "returns 404 error when specified user is not exist", %{conn: conn} do
712 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
714 assert json_response(conn, 404) == %{"error" => "Record not found"}
717 test "returns 403 error when user has hidden own favorites", %{
719 current_user: current_user
721 user = insert(:user, %{info: %{hide_favorites: true}})
722 activity = insert(:note_activity)
723 CommonAPI.favorite(activity.id, user)
727 |> assign(:user, current_user)
728 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
730 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
733 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
735 activity = insert(:note_activity)
736 CommonAPI.favorite(activity.id, user)
740 |> assign(:user, current_user)
741 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
743 assert user.info.hide_favorites
744 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
748 describe "pinned statuses" do
751 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
753 [user: user, activity: activity]
756 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
757 {:ok, _} = CommonAPI.pin(activity.id, user)
761 |> assign(:user, user)
762 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
763 |> json_response(200)
765 id_str = to_string(activity.id)
767 assert [%{"id" => ^id_str, "pinned" => true}] = result
771 test "subscribing / unsubscribing to a user", %{conn: conn} do
773 subscription_target = insert(:user)
777 |> assign(:user, user)
778 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
780 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
784 |> assign(:user, user)
785 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
787 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
790 test "blocking / unblocking a user", %{conn: conn} do
792 other_user = insert(:user)
796 |> assign(:user, user)
797 |> post("/api/v1/accounts/#{other_user.id}/block")
799 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
801 user = User.get_cached_by_id(user.id)
805 |> assign(:user, user)
806 |> post("/api/v1/accounts/#{other_user.id}/unblock")
808 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)