1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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
11 alias Pleroma.Web.ActivityPub.ActivityPub
12 alias Pleroma.Web.ActivityPub.InternalFetchActor
13 alias Pleroma.Web.CommonAPI
14 alias Pleroma.Web.OAuth.Token
16 import Pleroma.Factory
18 describe "account fetching" do
19 setup do: clear_config([:instance, :limit_to_local_content])
22 %User{id: user_id} = insert(:user)
24 assert %{"id" => ^user_id} =
26 |> get("/api/v1/accounts/#{user_id}")
27 |> json_response_and_validate_schema(200)
29 assert %{"error" => "Can't find user"} =
31 |> get("/api/v1/accounts/-1")
32 |> json_response_and_validate_schema(404)
35 test "works by nickname" do
38 assert %{"id" => user_id} =
40 |> get("/api/v1/accounts/#{user.nickname}")
41 |> json_response_and_validate_schema(200)
44 test "works by nickname for remote users" do
45 Config.put([:instance, :limit_to_local_content], false)
47 user = insert(:user, nickname: "user@example.com", local: false)
49 assert %{"id" => user_id} =
51 |> get("/api/v1/accounts/#{user.nickname}")
52 |> json_response_and_validate_schema(200)
55 test "respects limit_to_local_content == :all for remote user nicknames" do
56 Config.put([:instance, :limit_to_local_content], :all)
58 user = insert(:user, nickname: "user@example.com", local: false)
61 |> get("/api/v1/accounts/#{user.nickname}")
62 |> json_response_and_validate_schema(404)
65 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
66 Config.put([:instance, :limit_to_local_content], :unauthenticated)
68 user = insert(:user, nickname: "user@example.com", local: false)
69 reading_user = insert(:user)
73 |> get("/api/v1/accounts/#{user.nickname}")
75 assert json_response_and_validate_schema(conn, 404)
79 |> assign(:user, reading_user)
80 |> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"]))
81 |> get("/api/v1/accounts/#{user.nickname}")
83 assert %{"id" => id} = json_response_and_validate_schema(conn, 200)
87 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
88 # Need to set an old-style integer ID to reproduce the problem
89 # (these are no longer assigned to new accounts but were preserved
90 # for existing accounts during the migration to flakeIDs)
91 user_one = insert(:user, %{id: 1212})
92 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
96 |> get("/api/v1/accounts/#{user_one.id}")
97 |> json_response_and_validate_schema(:ok)
101 |> get("/api/v1/accounts/#{user_two.nickname}")
102 |> json_response_and_validate_schema(:ok)
106 |> get("/api/v1/accounts/#{user_two.id}")
107 |> json_response_and_validate_schema(:ok)
109 refute acc_one == acc_two
110 assert acc_two == acc_three
113 test "returns 404 when user is invisible", %{conn: conn} do
114 user = insert(:user, %{invisible: true})
116 assert %{"error" => "Can't find user"} =
118 |> get("/api/v1/accounts/#{user.nickname}")
119 |> json_response_and_validate_schema(404)
122 test "returns 404 for internal.fetch actor", %{conn: conn} do
123 %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor()
125 assert %{"error" => "Can't find user"} =
127 |> get("/api/v1/accounts/internal.fetch")
128 |> json_response_and_validate_schema(404)
132 defp local_and_remote_users do
133 local = insert(:user)
134 remote = insert(:user, local: false)
135 {:ok, local: local, remote: remote}
138 describe "user fetching with restrict unauthenticated profiles for local and remote" do
139 setup do: local_and_remote_users()
141 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
143 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
145 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
146 assert %{"error" => "Can't find user"} ==
148 |> get("/api/v1/accounts/#{local.id}")
149 |> json_response_and_validate_schema(:not_found)
151 assert %{"error" => "Can't find user"} ==
153 |> get("/api/v1/accounts/#{remote.id}")
154 |> json_response_and_validate_schema(:not_found)
157 test "if user is authenticated", %{local: local, remote: remote} do
158 %{conn: conn} = oauth_access(["read"])
160 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
161 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
163 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
164 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
168 describe "user fetching with restrict unauthenticated profiles for local" do
169 setup do: local_and_remote_users()
171 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
173 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
174 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
176 assert json_response_and_validate_schema(res_conn, :not_found) == %{
177 "error" => "Can't find user"
180 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
181 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
184 test "if user is authenticated", %{local: local, remote: remote} do
185 %{conn: conn} = oauth_access(["read"])
187 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
188 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
190 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
191 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
195 describe "user fetching with restrict unauthenticated profiles for remote" do
196 setup do: local_and_remote_users()
198 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
200 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
201 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
202 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
204 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
206 assert json_response_and_validate_schema(res_conn, :not_found) == %{
207 "error" => "Can't find user"
211 test "if user is authenticated", %{local: local, remote: remote} do
212 %{conn: conn} = oauth_access(["read"])
214 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
215 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
217 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
218 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
222 describe "user timelines" do
223 setup do: oauth_access(["read:statuses"])
225 test "respects blocks", %{user: user_one, conn: conn} do
226 user_two = insert(:user)
227 user_three = insert(:user)
229 User.block(user_one, user_two)
231 {:ok, activity} = CommonAPI.post(user_two, %{"status" => "User one sux0rz"})
232 {:ok, repeat, _} = CommonAPI.repeat(activity.id, user_three)
236 |> get("/api/v1/accounts/#{user_two.id}/statuses")
237 |> json_response_and_validate_schema(200)
239 assert [%{"id" => id}] = resp
240 assert id == activity.id
242 # Even a blocked user will deliver the full user timeline, there would be
243 # no point in looking at a blocked users timeline otherwise
246 |> get("/api/v1/accounts/#{user_two.id}/statuses")
247 |> json_response_and_validate_schema(200)
249 assert [%{"id" => id}] = resp
250 assert id == activity.id
252 # Third user's timeline includes the repeat when viewed by unauthenticated user
255 |> get("/api/v1/accounts/#{user_three.id}/statuses")
256 |> json_response_and_validate_schema(200)
258 assert [%{"id" => id}] = resp
259 assert id == repeat.id
261 # When viewing a third user's timeline, the blocked users' statuses will NOT be shown
262 resp = get(conn, "/api/v1/accounts/#{user_three.id}/statuses")
264 assert [] == json_response_and_validate_schema(resp, 200)
267 test "gets users statuses", %{conn: conn} do
268 user_one = insert(:user)
269 user_two = insert(:user)
270 user_three = insert(:user)
272 {:ok, _user_three} = User.follow(user_three, user_one)
274 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
276 {:ok, direct_activity} =
277 CommonAPI.post(user_one, %{
278 "status" => "Hi, @#{user_two.nickname}.",
279 "visibility" => "direct"
282 {:ok, private_activity} =
283 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
288 |> get("/api/v1/accounts/#{user_one.id}/statuses")
289 |> json_response_and_validate_schema(200)
291 assert [%{"id" => id}] = resp
292 assert id == to_string(activity.id)
296 |> assign(:user, user_two)
297 |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"]))
298 |> get("/api/v1/accounts/#{user_one.id}/statuses")
299 |> json_response_and_validate_schema(200)
301 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
302 assert id_one == to_string(direct_activity.id)
303 assert id_two == to_string(activity.id)
307 |> assign(:user, user_three)
308 |> assign(:token, insert(:oauth_token, user: user_three, scopes: ["read:statuses"]))
309 |> get("/api/v1/accounts/#{user_one.id}/statuses")
310 |> json_response_and_validate_schema(200)
312 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
313 assert id_one == to_string(private_activity.id)
314 assert id_two == to_string(activity.id)
317 test "unimplemented pinned statuses feature", %{conn: conn} do
318 note = insert(:note_activity)
319 user = User.get_cached_by_ap_id(note.data["actor"])
321 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?pinned=true")
323 assert json_response_and_validate_schema(conn, 200) == []
326 test "gets an users media", %{conn: conn} do
327 note = insert(:note_activity)
328 user = User.get_cached_by_ap_id(note.data["actor"])
331 content_type: "image/jpg",
332 path: Path.absname("test/fixtures/image.jpg"),
333 filename: "an_image.jpg"
336 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
338 {:ok, %{id: image_post_id}} =
339 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
341 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true")
343 assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
345 conn = get(build_conn(), "/api/v1/accounts/#{user.id}/statuses?only_media=1")
347 assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
350 test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
351 {:ok, %{id: post_id}} = CommonAPI.post(user, %{"status" => "HI!!!"})
352 {:ok, _, _} = CommonAPI.repeat(post_id, user)
354 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
355 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
357 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=1")
358 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
361 test "filters user's statuses by a hashtag", %{user: user, conn: conn} do
362 {:ok, %{id: post_id}} = CommonAPI.post(user, %{"status" => "#hashtag"})
363 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
365 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?tagged=hashtag")
366 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
369 test "the user views their own timelines and excludes direct messages", %{
373 {:ok, %{id: public_activity_id}} =
374 CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
376 {:ok, _direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
378 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_visibilities[]=direct")
379 assert [%{"id" => ^public_activity_id}] = json_response_and_validate_schema(conn, 200)
383 defp local_and_remote_activities(%{local: local, remote: remote}) do
384 insert(:note_activity, user: local)
385 insert(:note_activity, user: remote, local: false)
390 describe "statuses with restrict unauthenticated profiles for local and remote" do
391 setup do: local_and_remote_users()
392 setup :local_and_remote_activities
394 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
396 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
398 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
399 assert %{"error" => "Can't find user"} ==
401 |> get("/api/v1/accounts/#{local.id}/statuses")
402 |> json_response_and_validate_schema(:not_found)
404 assert %{"error" => "Can't find user"} ==
406 |> get("/api/v1/accounts/#{remote.id}/statuses")
407 |> json_response_and_validate_schema(:not_found)
410 test "if user is authenticated", %{local: local, remote: remote} do
411 %{conn: conn} = oauth_access(["read"])
413 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
414 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
416 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
417 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
421 describe "statuses with restrict unauthenticated profiles for local" do
422 setup do: local_and_remote_users()
423 setup :local_and_remote_activities
425 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
427 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
428 assert %{"error" => "Can't find user"} ==
430 |> get("/api/v1/accounts/#{local.id}/statuses")
431 |> json_response_and_validate_schema(:not_found)
433 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
434 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
437 test "if user is authenticated", %{local: local, remote: remote} do
438 %{conn: conn} = oauth_access(["read"])
440 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
441 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
443 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
444 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
448 describe "statuses with restrict unauthenticated profiles for remote" do
449 setup do: local_and_remote_users()
450 setup :local_and_remote_activities
452 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
454 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
455 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
456 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
458 assert %{"error" => "Can't find user"} ==
460 |> get("/api/v1/accounts/#{remote.id}/statuses")
461 |> json_response_and_validate_schema(:not_found)
464 test "if user is authenticated", %{local: local, remote: remote} do
465 %{conn: conn} = oauth_access(["read"])
467 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
468 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
470 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
471 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
475 describe "followers" do
476 setup do: oauth_access(["read:accounts"])
478 test "getting followers", %{user: user, conn: conn} do
479 other_user = insert(:user)
480 {:ok, %{id: user_id}} = User.follow(user, other_user)
482 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
484 assert [%{"id" => ^user_id}] = json_response_and_validate_schema(conn, 200)
487 test "getting followers, hide_followers", %{user: user, conn: conn} do
488 other_user = insert(:user, hide_followers: true)
489 {:ok, _user} = User.follow(user, other_user)
491 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
493 assert [] == json_response_and_validate_schema(conn, 200)
496 test "getting followers, hide_followers, same user requesting" do
498 other_user = insert(:user, hide_followers: true)
499 {:ok, _user} = User.follow(user, other_user)
503 |> assign(:user, other_user)
504 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
505 |> get("/api/v1/accounts/#{other_user.id}/followers")
507 refute [] == json_response_and_validate_schema(conn, 200)
510 test "getting followers, pagination", %{user: user, conn: conn} do
511 {:ok, %User{id: follower1_id}} = :user |> insert() |> User.follow(user)
512 {:ok, %User{id: follower2_id}} = :user |> insert() |> User.follow(user)
513 {:ok, %User{id: follower3_id}} = :user |> insert() |> User.follow(user)
515 assert [%{"id" => ^follower3_id}, %{"id" => ^follower2_id}] =
517 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1_id}")
518 |> json_response_and_validate_schema(200)
520 assert [%{"id" => ^follower2_id}, %{"id" => ^follower1_id}] =
522 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3_id}")
523 |> json_response_and_validate_schema(200)
525 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3_id}")
527 assert [%{"id" => ^follower2_id}] = json_response_and_validate_schema(res_conn, 200)
529 assert [link_header] = get_resp_header(res_conn, "link")
530 assert link_header =~ ~r/min_id=#{follower2_id}/
531 assert link_header =~ ~r/max_id=#{follower2_id}/
535 describe "following" do
536 setup do: oauth_access(["read:accounts"])
538 test "getting following", %{user: user, conn: conn} do
539 other_user = insert(:user)
540 {:ok, user} = User.follow(user, other_user)
542 conn = get(conn, "/api/v1/accounts/#{user.id}/following")
544 assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200)
545 assert id == to_string(other_user.id)
548 test "getting following, hide_follows, other user requesting" do
549 user = insert(:user, hide_follows: true)
550 other_user = insert(:user)
551 {:ok, user} = User.follow(user, other_user)
555 |> assign(:user, other_user)
556 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
557 |> get("/api/v1/accounts/#{user.id}/following")
559 assert [] == json_response_and_validate_schema(conn, 200)
562 test "getting following, hide_follows, same user requesting" do
563 user = insert(:user, hide_follows: true)
564 other_user = insert(:user)
565 {:ok, user} = User.follow(user, other_user)
569 |> assign(:user, user)
570 |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"]))
571 |> get("/api/v1/accounts/#{user.id}/following")
573 refute [] == json_response_and_validate_schema(conn, 200)
576 test "getting following, pagination", %{user: user, conn: conn} do
577 following1 = insert(:user)
578 following2 = insert(:user)
579 following3 = insert(:user)
580 {:ok, _} = User.follow(user, following1)
581 {:ok, _} = User.follow(user, following2)
582 {:ok, _} = User.follow(user, following3)
584 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
586 assert [%{"id" => id3}, %{"id" => id2}] = json_response_and_validate_schema(res_conn, 200)
587 assert id3 == following3.id
588 assert id2 == following2.id
590 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
592 assert [%{"id" => id2}, %{"id" => id1}] = json_response_and_validate_schema(res_conn, 200)
593 assert id2 == following2.id
594 assert id1 == following1.id
597 get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
599 assert [%{"id" => id2}] = json_response_and_validate_schema(res_conn, 200)
600 assert id2 == following2.id
602 assert [link_header] = get_resp_header(res_conn, "link")
603 assert link_header =~ ~r/min_id=#{following2.id}/
604 assert link_header =~ ~r/max_id=#{following2.id}/
608 describe "follow/unfollow" do
609 setup do: oauth_access(["follow"])
611 test "following / unfollowing a user", %{conn: conn} do
612 %{id: other_user_id, nickname: other_user_nickname} = insert(:user)
614 assert %{"id" => _id, "following" => true} =
616 |> post("/api/v1/accounts/#{other_user_id}/follow")
617 |> json_response_and_validate_schema(200)
619 assert %{"id" => _id, "following" => false} =
621 |> post("/api/v1/accounts/#{other_user_id}/unfollow")
622 |> json_response_and_validate_schema(200)
624 assert %{"id" => ^other_user_id} =
626 |> put_req_header("content-type", "application/json")
627 |> post("/api/v1/follows", %{"uri" => other_user_nickname})
628 |> json_response_and_validate_schema(200)
631 test "cancelling follow request", %{conn: conn} do
632 %{id: other_user_id} = insert(:user, %{locked: true})
634 assert %{"id" => ^other_user_id, "following" => false, "requested" => true} =
636 |> post("/api/v1/accounts/#{other_user_id}/follow")
637 |> json_response_and_validate_schema(:ok)
639 assert %{"id" => ^other_user_id, "following" => false, "requested" => false} =
641 |> post("/api/v1/accounts/#{other_user_id}/unfollow")
642 |> json_response_and_validate_schema(:ok)
645 test "following without reblogs" do
646 %{conn: conn} = oauth_access(["follow", "read:statuses"])
647 followed = insert(:user)
648 other_user = insert(:user)
650 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=false")
652 assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200)
654 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
655 {:ok, %{id: reblog_id}, _} = CommonAPI.repeat(activity.id, followed)
659 |> get("/api/v1/timelines/home")
660 |> json_response(200)
662 assert %{"showing_reblogs" => true} =
664 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
665 |> json_response_and_validate_schema(200)
667 assert [%{"id" => ^reblog_id}] =
669 |> get("/api/v1/timelines/home")
670 |> json_response(200)
673 test "following / unfollowing errors", %{user: user, conn: conn} do
675 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
677 assert %{"error" => "Can not follow yourself"} =
678 json_response_and_validate_schema(conn_res, 400)
681 user = User.get_cached_by_id(user.id)
682 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
684 assert %{"error" => "Can not unfollow yourself"} =
685 json_response_and_validate_schema(conn_res, 400)
687 # self follow via uri
688 user = User.get_cached_by_id(user.id)
690 assert %{"error" => "Can not follow yourself"} =
692 |> put_req_header("content-type", "multipart/form-data")
693 |> post("/api/v1/follows", %{"uri" => user.nickname})
694 |> json_response_and_validate_schema(400)
696 # follow non existing user
697 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
698 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
700 # follow non existing user via uri
703 |> put_req_header("content-type", "multipart/form-data")
704 |> post("/api/v1/follows", %{"uri" => "doesntexist"})
706 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
708 # unfollow non existing user
709 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
710 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
714 describe "mute/unmute" do
715 setup do: oauth_access(["write:mutes"])
717 test "with notifications", %{conn: conn} do
718 other_user = insert(:user)
720 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} =
722 |> put_req_header("content-type", "application/json")
723 |> post("/api/v1/accounts/#{other_user.id}/mute")
724 |> json_response_and_validate_schema(200)
726 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
728 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
729 json_response_and_validate_schema(conn, 200)
732 test "without notifications", %{conn: conn} do
733 other_user = insert(:user)
737 |> put_req_header("content-type", "multipart/form-data")
738 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
740 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} =
741 json_response_and_validate_schema(ret_conn, 200)
743 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
745 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
746 json_response_and_validate_schema(conn, 200)
750 describe "pinned statuses" do
753 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
754 %{conn: conn} = oauth_access(["read:statuses"], user: user)
756 [conn: conn, user: user, activity: activity]
759 test "returns pinned statuses", %{conn: conn, user: user, activity: %{id: activity_id}} do
760 {:ok, _} = CommonAPI.pin(activity_id, user)
762 assert [%{"id" => ^activity_id, "pinned" => true}] =
764 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
765 |> json_response_and_validate_schema(200)
769 test "blocking / unblocking a user" do
770 %{conn: conn} = oauth_access(["follow"])
771 other_user = insert(:user)
773 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block")
775 assert %{"id" => _id, "blocking" => true} = json_response_and_validate_schema(ret_conn, 200)
777 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock")
779 assert %{"id" => _id, "blocking" => false} = json_response_and_validate_schema(conn, 200)
782 describe "create account by app" do
786 email: "lain@example.org",
787 password: "PlzDontHackLain",
791 [valid_params: valid_params]
794 setup do: clear_config([:instance, :account_activation_required])
796 test "Account registration via Application", %{conn: conn} do
799 |> put_req_header("content-type", "application/json")
800 |> post("/api/v1/apps", %{
801 client_name: "client_name",
802 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
803 scopes: "read, write, follow"
807 "client_id" => client_id,
808 "client_secret" => client_secret,
810 "name" => "client_name",
811 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
814 } = json_response_and_validate_schema(conn, 200)
817 post(conn, "/oauth/token", %{
818 grant_type: "client_credentials",
819 client_id: client_id,
820 client_secret: client_secret
823 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
824 json_response(conn, 200)
827 token_from_db = Repo.get_by(Token, token: token)
830 assert scope == "read write follow"
834 |> put_req_header("content-type", "multipart/form-data")
835 |> put_req_header("authorization", "Bearer " <> token)
836 |> post("/api/v1/accounts", %{
838 email: "lain@example.org",
839 password: "PlzDontHackLain",
845 "access_token" => token,
846 "created_at" => _created_at,
848 "token_type" => "Bearer"
849 } = json_response_and_validate_schema(conn, 200)
851 token_from_db = Repo.get_by(Token, token: token)
853 token_from_db = Repo.preload(token_from_db, :user)
854 assert token_from_db.user
856 assert token_from_db.user.confirmation_pending
859 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
860 _user = insert(:user, email: "lain@example.org")
861 app_token = insert(:oauth_token, user: nil)
865 |> put_req_header("authorization", "Bearer " <> app_token.token)
866 |> put_req_header("content-type", "application/json")
867 |> post("/api/v1/accounts", valid_params)
869 assert json_response_and_validate_schema(res, 400) == %{
870 "error" => "{\"email\":[\"has already been taken\"]}"
874 test "returns bad_request if missing required params", %{
876 valid_params: valid_params
878 app_token = insert(:oauth_token, user: nil)
882 |> put_req_header("authorization", "Bearer " <> app_token.token)
883 |> put_req_header("content-type", "application/json")
885 res = post(conn, "/api/v1/accounts", valid_params)
886 assert json_response_and_validate_schema(res, 200)
888 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
889 |> Stream.zip(Map.delete(valid_params, :email))
890 |> Enum.each(fn {ip, {attr, _}} ->
893 |> Map.put(:remote_ip, ip)
894 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
895 |> json_response_and_validate_schema(400)
898 "error" => "Missing field: #{attr}.",
901 "message" => "Missing field: #{attr}",
902 "source" => %{"pointer" => "/#{attr}"},
903 "title" => "Invalid value"
910 setup do: clear_config([:instance, :account_activation_required])
912 test "returns bad_request if missing email params when :account_activation_required is enabled",
913 %{conn: conn, valid_params: valid_params} do
914 Pleroma.Config.put([:instance, :account_activation_required], true)
916 app_token = insert(:oauth_token, user: nil)
920 |> put_req_header("authorization", "Bearer " <> app_token.token)
921 |> put_req_header("content-type", "application/json")
925 |> Map.put(:remote_ip, {127, 0, 0, 5})
926 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
928 assert json_response_and_validate_schema(res, 400) ==
929 %{"error" => "Missing parameter: email"}
933 |> Map.put(:remote_ip, {127, 0, 0, 6})
934 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
936 assert json_response_and_validate_schema(res, 400) == %{
937 "error" => "{\"email\":[\"can't be blank\"]}"
941 test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
942 app_token = insert(:oauth_token, user: nil)
943 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
947 |> put_req_header("content-type", "application/json")
948 |> Map.put(:remote_ip, {127, 0, 0, 7})
949 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
951 assert json_response_and_validate_schema(res, 200)
954 test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
955 app_token = insert(:oauth_token, user: nil)
956 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
960 |> put_req_header("content-type", "application/json")
961 |> Map.put(:remote_ip, {127, 0, 0, 8})
962 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
964 assert json_response_and_validate_schema(res, 200)
967 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
970 |> put_req_header("authorization", "Bearer " <> "invalid-token")
971 |> put_req_header("content-type", "multipart/form-data")
972 |> post("/api/v1/accounts", valid_params)
974 assert json_response_and_validate_schema(res, 403) == %{"error" => "Invalid credentials"}
977 test "registration from trusted app" do
978 clear_config([Pleroma.Captcha, :enabled], true)
979 app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
983 |> post("/oauth/token", %{
984 "grant_type" => "client_credentials",
985 "client_id" => app.client_id,
986 "client_secret" => app.client_secret
989 assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
993 |> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
994 |> put_req_header("content-type", "multipart/form-data")
995 |> post("/api/v1/accounts", %{
996 nickname: "nickanme",
998 email: "email@example.com",
1001 password: "some_password",
1002 confirm: "some_password"
1004 |> json_response_and_validate_schema(200)
1007 "access_token" => access_token,
1009 "scope" => ["read", "write", "follow", "push"],
1010 "token_type" => "Bearer"
1015 |> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
1016 |> get("/api/v1/accounts/verify_credentials")
1017 |> json_response_and_validate_schema(200)
1022 "display_name" => "Lain",
1023 "follow_requests_count" => 0,
1024 "followers_count" => 0,
1025 "following_count" => 0,
1032 "actor_type" => "Person",
1033 "discoverable" => false,
1034 "no_rich_text" => false,
1037 "privacy" => "public",
1038 "sensitive" => false
1040 "statuses_count" => 0,
1041 "username" => "Lain"
1046 describe "create account by app / rate limit" do
1047 setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
1049 test "respects rate limit setting", %{conn: conn} do
1050 app_token = insert(:oauth_token, user: nil)
1054 |> put_req_header("authorization", "Bearer " <> app_token.token)
1055 |> Map.put(:remote_ip, {15, 15, 15, 15})
1056 |> put_req_header("content-type", "multipart/form-data")
1061 |> post("/api/v1/accounts", %{
1062 username: "#{i}lain",
1063 email: "#{i}lain@example.org",
1064 password: "PlzDontHackLain",
1069 "access_token" => token,
1070 "created_at" => _created_at,
1072 "token_type" => "Bearer"
1073 } = json_response_and_validate_schema(conn, 200)
1075 token_from_db = Repo.get_by(Token, token: token)
1076 assert token_from_db
1077 token_from_db = Repo.preload(token_from_db, :user)
1078 assert token_from_db.user
1080 assert token_from_db.user.confirmation_pending
1084 post(conn, "/api/v1/accounts", %{
1086 email: "6lain@example.org",
1087 password: "PlzDontHackLain",
1091 assert json_response_and_validate_schema(conn, :too_many_requests) == %{
1092 "error" => "Throttled"
1097 describe "create account with enabled captcha" do
1098 setup %{conn: conn} do
1099 app_token = insert(:oauth_token, user: nil)
1103 |> put_req_header("authorization", "Bearer " <> app_token.token)
1104 |> put_req_header("content-type", "multipart/form-data")
1109 setup do: clear_config([Pleroma.Captcha, :enabled], true)
1111 test "creates an account and returns 200 if captcha is valid", %{conn: conn} do
1112 %{token: token, answer_data: answer_data} = Pleroma.Captcha.new()
1116 email: "lain@example.org",
1117 password: "PlzDontHackLain",
1119 captcha_solution: Pleroma.Captcha.Mock.solution(),
1120 captcha_token: token,
1121 captcha_answer_data: answer_data
1125 "access_token" => access_token,
1127 "scope" => ["read"],
1128 "token_type" => "Bearer"
1131 |> post("/api/v1/accounts", params)
1132 |> json_response_and_validate_schema(:ok)
1134 assert Token |> Repo.get_by(token: access_token) |> Repo.preload(:user) |> Map.get(:user)
1136 Cachex.del(:used_captcha_cache, token)
1139 test "returns 400 if any captcha field is not provided", %{conn: conn} do
1140 captcha_fields = [:captcha_solution, :captcha_token, :captcha_answer_data]
1144 email: "lain@example.org",
1145 password: "PlzDontHackLain",
1147 captcha_solution: "xx",
1148 captcha_token: "xx",
1149 captcha_answer_data: "xx"
1152 for field <- captcha_fields do
1154 "error" => "{\"captcha\":[\"Invalid CAPTCHA (Missing parameter: #{field})\"]}"
1159 |> post("/api/v1/accounts", Map.delete(valid_params, field))
1160 |> json_response_and_validate_schema(:bad_request)
1164 test "returns an error if captcha is invalid", %{conn: conn} do
1167 email: "lain@example.org",
1168 password: "PlzDontHackLain",
1170 captcha_solution: "cofe",
1171 captcha_token: "cofe",
1172 captcha_answer_data: "cofe"
1175 assert %{"error" => "{\"captcha\":[\"Invalid answer data\"]}"} ==
1177 |> post("/api/v1/accounts", params)
1178 |> json_response_and_validate_schema(:bad_request)
1182 describe "GET /api/v1/accounts/:id/lists - account_lists" do
1183 test "returns lists to which the account belongs" do
1184 %{user: user, conn: conn} = oauth_access(["read:lists"])
1185 other_user = insert(:user)
1186 assert {:ok, %Pleroma.List{id: list_id} = list} = Pleroma.List.create("Test List", user)
1187 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
1189 assert [%{"id" => list_id, "title" => "Test List"}] =
1191 |> get("/api/v1/accounts/#{other_user.id}/lists")
1192 |> json_response_and_validate_schema(200)
1196 describe "verify_credentials" do
1197 test "verify_credentials" do
1198 %{user: user, conn: conn} = oauth_access(["read:accounts"])
1199 conn = get(conn, "/api/v1/accounts/verify_credentials")
1201 response = json_response_and_validate_schema(conn, 200)
1203 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
1204 assert response["pleroma"]["chat_token"]
1205 assert id == to_string(user.id)
1208 test "verify_credentials default scope unlisted" do
1209 user = insert(:user, default_scope: "unlisted")
1210 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1212 conn = get(conn, "/api/v1/accounts/verify_credentials")
1214 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} =
1215 json_response_and_validate_schema(conn, 200)
1217 assert id == to_string(user.id)
1220 test "locked accounts" do
1221 user = insert(:user, default_scope: "private")
1222 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1224 conn = get(conn, "/api/v1/accounts/verify_credentials")
1226 assert %{"id" => id, "source" => %{"privacy" => "private"}} =
1227 json_response_and_validate_schema(conn, 200)
1229 assert id == to_string(user.id)
1233 describe "user relationships" do
1234 setup do: oauth_access(["read:follows"])
1236 test "returns the relationships for the current user", %{user: user, conn: conn} do
1237 %{id: other_user_id} = other_user = insert(:user)
1238 {:ok, _user} = User.follow(user, other_user)
1240 assert [%{"id" => ^other_user_id}] =
1242 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1243 |> json_response_and_validate_schema(200)
1245 assert [%{"id" => ^other_user_id}] =
1247 |> get("/api/v1/accounts/relationships?id[]=#{other_user.id}")
1248 |> json_response_and_validate_schema(200)
1251 test "returns an empty list on a bad request", %{conn: conn} do
1252 conn = get(conn, "/api/v1/accounts/relationships", %{})
1254 assert [] = json_response_and_validate_schema(conn, 200)
1258 test "getting a list of mutes" do
1259 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1260 other_user = insert(:user)
1262 {:ok, _user_relationships} = User.mute(user, other_user)
1264 conn = get(conn, "/api/v1/mutes")
1266 other_user_id = to_string(other_user.id)
1267 assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200)
1270 test "getting a list of blocks" do
1271 %{user: user, conn: conn} = oauth_access(["read:blocks"])
1272 other_user = insert(:user)
1274 {:ok, _user_relationship} = User.block(user, other_user)
1278 |> assign(:user, user)
1279 |> get("/api/v1/blocks")
1281 other_user_id = to_string(other_user.id)
1282 assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200)