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.ApiSpec
14 alias Pleroma.Web.CommonAPI
15 alias Pleroma.Web.OAuth.Token
17 import OpenApiSpex.TestAssertions
18 import Pleroma.Factory
20 describe "account fetching" do
21 setup do: clear_config([:instance, :limit_to_local_content])
28 |> get("/api/v1/accounts/#{user.id}")
30 assert %{"id" => id} = json_response(conn, 200)
31 assert id == to_string(user.id)
35 |> get("/api/v1/accounts/-1")
37 assert %{"error" => "Can't find user"} = json_response(conn, 404)
40 test "works by nickname" do
45 |> get("/api/v1/accounts/#{user.nickname}")
47 assert %{"id" => id} = json_response(conn, 200)
51 test "works by nickname for remote users" do
52 Config.put([:instance, :limit_to_local_content], false)
53 user = insert(:user, nickname: "user@example.com", local: false)
57 |> get("/api/v1/accounts/#{user.nickname}")
59 assert %{"id" => id} = json_response(conn, 200)
63 test "respects limit_to_local_content == :all for remote user nicknames" do
64 Config.put([:instance, :limit_to_local_content], :all)
66 user = insert(:user, nickname: "user@example.com", local: false)
70 |> get("/api/v1/accounts/#{user.nickname}")
72 assert json_response(conn, 404)
75 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
76 Config.put([:instance, :limit_to_local_content], :unauthenticated)
78 user = insert(:user, nickname: "user@example.com", local: false)
79 reading_user = insert(:user)
83 |> get("/api/v1/accounts/#{user.nickname}")
85 assert json_response(conn, 404)
89 |> assign(:user, reading_user)
90 |> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"]))
91 |> get("/api/v1/accounts/#{user.nickname}")
93 assert %{"id" => id} = json_response(conn, 200)
97 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
98 # Need to set an old-style integer ID to reproduce the problem
99 # (these are no longer assigned to new accounts but were preserved
100 # for existing accounts during the migration to flakeIDs)
101 user_one = insert(:user, %{id: 1212})
102 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
106 |> get("/api/v1/accounts/#{user_one.id}")
110 |> get("/api/v1/accounts/#{user_two.nickname}")
114 |> get("/api/v1/accounts/#{user_two.id}")
116 acc_one = json_response(resp_one, 200)
117 acc_two = json_response(resp_two, 200)
118 acc_three = json_response(resp_three, 200)
119 refute acc_one == acc_two
120 assert acc_two == acc_three
123 test "returns 404 when user is invisible", %{conn: conn} do
124 user = insert(:user, %{invisible: true})
128 |> get("/api/v1/accounts/#{user.nickname}")
129 |> json_response(404)
131 assert %{"error" => "Can't find user"} = resp
134 test "returns 404 for internal.fetch actor", %{conn: conn} do
135 %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor()
139 |> get("/api/v1/accounts/internal.fetch")
140 |> json_response(404)
142 assert %{"error" => "Can't find user"} = resp
146 defp local_and_remote_users do
147 local = insert(:user)
148 remote = insert(:user, local: false)
149 {:ok, local: local, remote: remote}
152 describe "user fetching with restrict unauthenticated profiles for local and remote" do
153 setup do: local_and_remote_users()
155 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
157 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
159 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
160 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
162 assert json_response(res_conn, :not_found) == %{
163 "error" => "Can't find user"
166 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
168 assert json_response(res_conn, :not_found) == %{
169 "error" => "Can't find user"
173 test "if user is authenticated", %{local: local, remote: remote} do
174 %{conn: conn} = oauth_access(["read"])
176 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
177 assert %{"id" => _} = json_response(res_conn, 200)
179 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
180 assert %{"id" => _} = json_response(res_conn, 200)
184 describe "user fetching with restrict unauthenticated profiles for local" do
185 setup do: local_and_remote_users()
187 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
189 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
190 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
192 assert json_response(res_conn, :not_found) == %{
193 "error" => "Can't find user"
196 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
197 assert %{"id" => _} = json_response(res_conn, 200)
200 test "if user is authenticated", %{local: local, remote: remote} do
201 %{conn: conn} = oauth_access(["read"])
203 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
204 assert %{"id" => _} = json_response(res_conn, 200)
206 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
207 assert %{"id" => _} = json_response(res_conn, 200)
211 describe "user fetching with restrict unauthenticated profiles for remote" do
212 setup do: local_and_remote_users()
214 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
216 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
217 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
218 assert %{"id" => _} = json_response(res_conn, 200)
220 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
222 assert json_response(res_conn, :not_found) == %{
223 "error" => "Can't find user"
227 test "if user is authenticated", %{local: local, remote: remote} do
228 %{conn: conn} = oauth_access(["read"])
230 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
231 assert %{"id" => _} = json_response(res_conn, 200)
233 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
234 assert %{"id" => _} = json_response(res_conn, 200)
238 describe "user timelines" do
239 setup do: oauth_access(["read:statuses"])
241 test "respects blocks", %{user: user_one, conn: conn} do
242 user_two = insert(:user)
243 user_three = insert(:user)
245 User.block(user_one, user_two)
247 {:ok, activity} = CommonAPI.post(user_two, %{"status" => "User one sux0rz"})
248 {:ok, repeat, _} = CommonAPI.repeat(activity.id, user_three)
250 assert resp = get(conn, "/api/v1/accounts/#{user_two.id}/statuses") |> json_response(200)
251 assert [%{"id" => id}] = resp
252 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
253 assert id == activity.id
255 # Even a blocked user will deliver the full user timeline, there would be
256 # no point in looking at a blocked users timeline otherwise
257 assert resp = get(conn, "/api/v1/accounts/#{user_two.id}/statuses") |> json_response(200)
258 assert [%{"id" => id}] = resp
259 assert id == activity.id
260 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
262 # Third user's timeline includes the repeat when viewed by unauthenticated user
263 resp = get(build_conn(), "/api/v1/accounts/#{user_three.id}/statuses") |> json_response(200)
264 assert [%{"id" => id}] = resp
265 assert id == repeat.id
266 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
268 # When viewing a third user's timeline, the blocked users' statuses will NOT be shown
269 resp = get(conn, "/api/v1/accounts/#{user_three.id}/statuses")
271 assert [] = json_response(resp, 200)
274 test "gets users statuses", %{conn: conn} do
275 user_one = insert(:user)
276 user_two = insert(:user)
277 user_three = insert(:user)
279 {:ok, _user_three} = User.follow(user_three, user_one)
281 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
283 {:ok, direct_activity} =
284 CommonAPI.post(user_one, %{
285 "status" => "Hi, @#{user_two.nickname}.",
286 "visibility" => "direct"
289 {:ok, private_activity} =
290 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
292 resp = get(conn, "/api/v1/accounts/#{user_one.id}/statuses") |> json_response(200)
293 assert [%{"id" => id}] = resp
294 assert id == to_string(activity.id)
295 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
299 |> assign(:user, user_two)
300 |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"]))
301 |> get("/api/v1/accounts/#{user_one.id}/statuses")
302 |> json_response(200)
304 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
305 assert id_one == to_string(direct_activity.id)
306 assert id_two == to_string(activity.id)
307 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
311 |> assign(:user, user_three)
312 |> assign(:token, insert(:oauth_token, user: user_three, scopes: ["read:statuses"]))
313 |> get("/api/v1/accounts/#{user_one.id}/statuses")
314 |> json_response(200)
316 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
317 assert id_one == to_string(private_activity.id)
318 assert id_two == to_string(activity.id)
319 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
322 test "unimplemented pinned statuses feature", %{conn: conn} do
323 note = insert(:note_activity)
324 user = User.get_cached_by_ap_id(note.data["actor"])
326 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?pinned=true")
328 assert json_response(conn, 200) == []
331 test "gets an users media", %{conn: conn} do
332 note = insert(:note_activity)
333 user = User.get_cached_by_ap_id(note.data["actor"])
336 content_type: "image/jpg",
337 path: Path.absname("test/fixtures/image.jpg"),
338 filename: "an_image.jpg"
341 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
343 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
345 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true")
347 assert [%{"id" => id}] = json_response(conn, 200)
348 assert id == to_string(image_post.id)
349 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
351 conn = get(build_conn(), "/api/v1/accounts/#{user.id}/statuses?only_media=1")
353 assert [%{"id" => id}] = json_response(conn, 200)
354 assert id == to_string(image_post.id)
355 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
358 test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
359 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
360 {:ok, _, _} = CommonAPI.repeat(post.id, user)
362 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
364 assert [%{"id" => id}] = json_response(conn, 200)
365 assert id == to_string(post.id)
366 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
368 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=1")
370 assert [%{"id" => id}] = json_response(conn, 200)
371 assert id == to_string(post.id)
372 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
375 test "filters user's statuses by a hashtag", %{user: user, conn: conn} do
376 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
377 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
379 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?tagged=hashtag")
381 assert [%{"id" => id}] = json_response(conn, 200)
382 assert id == to_string(post.id)
383 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
386 test "the user views their own timelines and excludes direct messages", %{
390 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
391 {:ok, _direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
393 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_visibilities[]=direct")
395 assert [%{"id" => id}] = json_response(conn, 200)
396 assert id == to_string(public_activity.id)
397 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
401 defp local_and_remote_activities(%{local: local, remote: remote}) do
402 insert(:note_activity, user: local)
403 insert(:note_activity, user: remote, local: false)
408 describe "statuses with restrict unauthenticated profiles for local and remote" do
409 setup do: local_and_remote_users()
410 setup :local_and_remote_activities
412 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
414 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
416 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
417 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
419 assert json_response(res_conn, :not_found) == %{
420 "error" => "Can't find user"
423 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
425 assert json_response(res_conn, :not_found) == %{
426 "error" => "Can't find user"
430 test "if user is authenticated", %{local: local, remote: remote} do
431 %{conn: conn} = oauth_access(["read"])
433 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
434 assert length(json_response(res_conn, 200)) == 1
435 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
437 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
438 assert length(json_response(res_conn, 200)) == 1
439 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
443 describe "statuses with restrict unauthenticated profiles for local" do
444 setup do: local_and_remote_users()
445 setup :local_and_remote_activities
447 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
449 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
450 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
452 assert json_response(res_conn, :not_found) == %{
453 "error" => "Can't find user"
456 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
457 assert length(json_response(res_conn, 200)) == 1
458 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
461 test "if user is authenticated", %{local: local, remote: remote} do
462 %{conn: conn} = oauth_access(["read"])
464 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
465 assert length(json_response(res_conn, 200)) == 1
466 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
468 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
469 assert length(json_response(res_conn, 200)) == 1
470 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
474 describe "statuses with restrict unauthenticated profiles for remote" do
475 setup do: local_and_remote_users()
476 setup :local_and_remote_activities
478 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
480 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
481 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
482 assert length(json_response(res_conn, 200)) == 1
483 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
485 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
487 assert json_response(res_conn, :not_found) == %{
488 "error" => "Can't find user"
492 test "if user is authenticated", %{local: local, remote: remote} do
493 %{conn: conn} = oauth_access(["read"])
495 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
496 assert length(json_response(res_conn, 200)) == 1
497 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
499 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
500 assert length(json_response(res_conn, 200)) == 1
501 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
505 describe "followers" do
506 setup do: oauth_access(["read:accounts"])
508 test "getting followers", %{user: user, conn: conn} do
509 other_user = insert(:user)
510 {:ok, user} = User.follow(user, other_user)
512 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
514 assert [%{"id" => id}] = json_response(conn, 200)
515 assert id == to_string(user.id)
516 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
519 test "getting followers, hide_followers", %{user: user, conn: conn} do
520 other_user = insert(:user, hide_followers: true)
521 {:ok, _user} = User.follow(user, other_user)
523 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
525 assert [] == json_response(conn, 200)
528 test "getting followers, hide_followers, same user requesting" do
530 other_user = insert(:user, hide_followers: true)
531 {:ok, _user} = User.follow(user, other_user)
535 |> assign(:user, other_user)
536 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
537 |> get("/api/v1/accounts/#{other_user.id}/followers")
539 refute [] == json_response(conn, 200)
540 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
543 test "getting followers, pagination", %{user: user, conn: conn} do
544 follower1 = insert(:user)
545 follower2 = insert(:user)
546 follower3 = insert(:user)
547 {:ok, _} = User.follow(follower1, user)
548 {:ok, _} = User.follow(follower2, user)
549 {:ok, _} = User.follow(follower3, user)
551 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
553 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
554 assert id3 == follower3.id
555 assert id2 == follower2.id
556 assert_schema(json_response(res_conn, 200), "AccountsResponse", ApiSpec.spec())
558 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
560 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
561 assert id2 == follower2.id
562 assert id1 == follower1.id
564 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
566 assert [%{"id" => id2}] = json_response(res_conn, 200)
567 assert id2 == follower2.id
569 assert [link_header] = get_resp_header(res_conn, "link")
570 assert link_header =~ ~r/min_id=#{follower2.id}/
571 assert link_header =~ ~r/max_id=#{follower2.id}/
572 assert_schema(json_response(res_conn, 200), "AccountsResponse", ApiSpec.spec())
576 describe "following" do
577 setup do: oauth_access(["read:accounts"])
579 test "getting following", %{user: user, conn: conn} do
580 other_user = insert(:user)
581 {:ok, user} = User.follow(user, other_user)
583 conn = get(conn, "/api/v1/accounts/#{user.id}/following")
585 assert [%{"id" => id}] = json_response(conn, 200)
586 assert id == to_string(other_user.id)
587 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
590 test "getting following, hide_follows, other user requesting" do
591 user = insert(:user, hide_follows: true)
592 other_user = insert(:user)
593 {:ok, user} = User.follow(user, other_user)
597 |> assign(:user, other_user)
598 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
599 |> get("/api/v1/accounts/#{user.id}/following")
601 assert [] == json_response(conn, 200)
602 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
605 test "getting following, hide_follows, same user requesting" do
606 user = insert(:user, hide_follows: true)
607 other_user = insert(:user)
608 {:ok, user} = User.follow(user, other_user)
612 |> assign(:user, user)
613 |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"]))
614 |> get("/api/v1/accounts/#{user.id}/following")
616 refute [] == json_response(conn, 200)
619 test "getting following, pagination", %{user: user, conn: conn} do
620 following1 = insert(:user)
621 following2 = insert(:user)
622 following3 = insert(:user)
623 {:ok, _} = User.follow(user, following1)
624 {:ok, _} = User.follow(user, following2)
625 {:ok, _} = User.follow(user, following3)
627 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
629 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
630 assert id3 == following3.id
631 assert id2 == following2.id
632 assert_schema(json_response(res_conn, 200), "AccountsResponse", ApiSpec.spec())
634 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
636 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
637 assert id2 == following2.id
638 assert id1 == following1.id
639 assert_schema(json_response(res_conn, 200), "AccountsResponse", ApiSpec.spec())
642 get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
644 assert [%{"id" => id2}] = json_response(res_conn, 200)
645 assert id2 == following2.id
647 assert [link_header] = get_resp_header(res_conn, "link")
648 assert link_header =~ ~r/min_id=#{following2.id}/
649 assert link_header =~ ~r/max_id=#{following2.id}/
650 assert_schema(json_response(res_conn, 200), "AccountsResponse", ApiSpec.spec())
654 describe "follow/unfollow" do
655 setup do: oauth_access(["follow"])
657 test "following / unfollowing a user", %{conn: conn} do
658 other_user = insert(:user)
660 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/follow")
662 assert %{"id" => _id, "following" => true} = json_response(ret_conn, 200)
663 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
665 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/unfollow")
667 assert %{"id" => _id, "following" => false} = json_response(ret_conn, 200)
668 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
672 |> put_req_header("content-type", "application/json")
673 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
675 assert %{"id" => id} = json_response(conn, 200)
676 assert id == to_string(other_user.id)
677 assert_schema(json_response(conn, 200), "Account", ApiSpec.spec())
680 test "cancelling follow request", %{conn: conn} do
681 %{id: other_user_id} = insert(:user, %{locked: true})
683 resp = conn |> post("/api/v1/accounts/#{other_user_id}/follow") |> json_response(:ok)
685 assert %{"id" => ^other_user_id, "following" => false, "requested" => true} = resp
686 assert_schema(resp, "AccountRelationship", ApiSpec.spec())
688 resp = conn |> post("/api/v1/accounts/#{other_user_id}/unfollow") |> json_response(:ok)
690 assert %{"id" => ^other_user_id, "following" => false, "requested" => false} = resp
691 assert_schema(resp, "AccountRelationship", ApiSpec.spec())
694 test "following without reblogs" do
695 %{conn: conn} = oauth_access(["follow", "read:statuses"])
696 followed = insert(:user)
697 other_user = insert(:user)
699 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=false")
701 assert %{"showing_reblogs" => false} = json_response(ret_conn, 200)
702 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
704 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
705 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
707 ret_conn = get(conn, "/api/v1/timelines/home")
709 assert [] == json_response(ret_conn, 200)
711 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=true")
713 assert %{"showing_reblogs" => true} = json_response(ret_conn, 200)
714 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
716 conn = get(conn, "/api/v1/timelines/home")
718 expected_activity_id = reblog.id
719 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
722 test "following / unfollowing errors", %{user: user, conn: conn} do
724 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
725 assert %{"error" => "Can not follow yourself"} = json_response(conn_res, 400)
728 user = User.get_cached_by_id(user.id)
729 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
730 assert %{"error" => "Can not unfollow yourself"} = json_response(conn_res, 400)
732 # self follow via uri
733 user = User.get_cached_by_id(user.id)
737 |> put_req_header("content-type", "multipart/form-data")
738 |> post("/api/v1/follows", %{"uri" => user.nickname})
740 assert %{"error" => "Can not follow yourself"} = json_response(conn_res, 400)
742 # follow non existing user
743 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
744 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
746 # follow non existing user via uri
749 |> put_req_header("content-type", "multipart/form-data")
750 |> post("/api/v1/follows", %{"uri" => "doesntexist"})
752 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
754 # unfollow non existing user
755 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
756 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
760 describe "mute/unmute" do
761 setup do: oauth_access(["write:mutes"])
763 test "with notifications", %{conn: conn} do
764 other_user = insert(:user)
768 |> put_req_header("content-type", "application/json")
769 |> post("/api/v1/accounts/#{other_user.id}/mute")
771 response = json_response(ret_conn, 200)
773 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
774 assert_schema(response, "AccountRelationship", ApiSpec.spec())
776 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
778 response = json_response(conn, 200)
779 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
780 assert_schema(response, "AccountRelationship", ApiSpec.spec())
783 test "without notifications", %{conn: conn} do
784 other_user = insert(:user)
788 |> put_req_header("content-type", "multipart/form-data")
789 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
791 response = json_response(ret_conn, 200)
793 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
794 assert_schema(response, "AccountRelationship", ApiSpec.spec())
796 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
798 response = json_response(conn, 200)
799 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
800 assert_schema(response, "AccountRelationship", ApiSpec.spec())
804 describe "pinned statuses" do
807 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
808 %{conn: conn} = oauth_access(["read:statuses"], user: user)
810 [conn: conn, user: user, activity: activity]
813 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
814 {:ok, _} = CommonAPI.pin(activity.id, user)
818 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
819 |> json_response(200)
821 id_str = to_string(activity.id)
823 assert [%{"id" => ^id_str, "pinned" => true}] = result
827 test "blocking / unblocking a user" do
828 %{conn: conn} = oauth_access(["follow"])
829 other_user = insert(:user)
831 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block")
833 assert %{"id" => _id, "blocking" => true} = json_response(ret_conn, 200)
834 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
836 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock")
838 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
839 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
842 describe "create account by app" do
846 email: "lain@example.org",
847 password: "PlzDontHackLain",
851 [valid_params: valid_params]
854 setup do: clear_config([:instance, :account_activation_required])
856 test "Account registration via Application", %{conn: conn} do
859 |> put_req_header("content-type", "application/json")
860 |> post("/api/v1/apps", %{
861 client_name: "client_name",
862 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
863 scopes: "read, write, follow"
867 "client_id" => client_id,
868 "client_secret" => client_secret,
870 "name" => "client_name",
871 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
874 } = json_response(conn, 200)
877 post(conn, "/oauth/token", %{
878 grant_type: "client_credentials",
879 client_id: client_id,
880 client_secret: client_secret
883 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
884 json_response(conn, 200)
887 token_from_db = Repo.get_by(Token, token: token)
890 assert scope == "read write follow"
894 |> put_req_header("content-type", "multipart/form-data")
895 |> put_req_header("authorization", "Bearer " <> token)
896 |> post("/api/v1/accounts", %{
898 email: "lain@example.org",
899 password: "PlzDontHackLain",
905 "access_token" => token,
906 "created_at" => _created_at,
908 "token_type" => "Bearer"
909 } = json_response(conn, 200)
911 token_from_db = Repo.get_by(Token, token: token)
913 token_from_db = Repo.preload(token_from_db, :user)
914 assert token_from_db.user
916 assert token_from_db.user.confirmation_pending
919 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
920 _user = insert(:user, email: "lain@example.org")
921 app_token = insert(:oauth_token, user: nil)
925 |> put_req_header("authorization", "Bearer " <> app_token.token)
926 |> put_req_header("content-type", "application/json")
927 |> post("/api/v1/accounts", valid_params)
929 assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
932 test "returns bad_request if missing required params", %{
934 valid_params: valid_params
936 app_token = insert(:oauth_token, user: nil)
940 |> put_req_header("authorization", "Bearer " <> app_token.token)
941 |> put_req_header("content-type", "application/json")
943 res = post(conn, "/api/v1/accounts", valid_params)
944 assert json_response(res, 200)
946 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
947 |> Stream.zip(Map.delete(valid_params, :email))
948 |> Enum.each(fn {ip, {attr, _}} ->
951 |> Map.put(:remote_ip, ip)
952 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
953 |> json_response(400)
956 "error" => "Missing field: #{attr}.",
959 "message" => "Missing field: #{attr}",
960 "source" => %{"pointer" => "/#{attr}"},
961 "title" => "Invalid value"
968 setup do: clear_config([:instance, :account_activation_required])
970 test "returns bad_request if missing email params when :account_activation_required is enabled",
971 %{conn: conn, valid_params: valid_params} do
972 Pleroma.Config.put([:instance, :account_activation_required], true)
974 app_token = insert(:oauth_token, user: nil)
978 |> put_req_header("authorization", "Bearer " <> app_token.token)
979 |> put_req_header("content-type", "application/json")
983 |> Map.put(:remote_ip, {127, 0, 0, 5})
984 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
986 assert json_response(res, 400) == %{"error" => "Missing parameters"}
990 |> Map.put(:remote_ip, {127, 0, 0, 6})
991 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
993 assert json_response(res, 400) == %{"error" => "{\"email\":[\"can't be blank\"]}"}
996 test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
997 app_token = insert(:oauth_token, user: nil)
998 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1002 |> put_req_header("content-type", "application/json")
1003 |> Map.put(:remote_ip, {127, 0, 0, 7})
1004 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
1006 assert json_response(res, 200)
1009 test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
1010 app_token = insert(:oauth_token, user: nil)
1011 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1015 |> put_req_header("content-type", "application/json")
1016 |> Map.put(:remote_ip, {127, 0, 0, 8})
1017 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
1019 assert json_response(res, 200)
1022 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
1025 |> put_req_header("authorization", "Bearer " <> "invalid-token")
1026 |> put_req_header("content-type", "multipart/form-data")
1027 |> post("/api/v1/accounts", valid_params)
1029 assert json_response(res, 403) == %{"error" => "Invalid credentials"}
1032 test "registration from trusted app" do
1033 clear_config([Pleroma.Captcha, :enabled], true)
1034 app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
1038 |> post("/oauth/token", %{
1039 "grant_type" => "client_credentials",
1040 "client_id" => app.client_id,
1041 "client_secret" => app.client_secret
1044 assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
1048 |> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
1049 |> put_req_header("content-type", "multipart/form-data")
1050 |> post("/api/v1/accounts", %{
1051 nickname: "nickanme",
1053 email: "email@example.com",
1056 password: "some_password",
1057 confirm: "some_password"
1059 |> json_response(200)
1062 "access_token" => access_token,
1064 "scope" => ["read", "write", "follow", "push"],
1065 "token_type" => "Bearer"
1070 |> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
1071 |> get("/api/v1/accounts/verify_credentials")
1072 |> json_response(200)
1077 "display_name" => "Lain",
1078 "follow_requests_count" => 0,
1079 "followers_count" => 0,
1080 "following_count" => 0,
1087 "actor_type" => "Person",
1088 "discoverable" => false,
1089 "no_rich_text" => false,
1092 "privacy" => "public",
1093 "sensitive" => false
1095 "statuses_count" => 0,
1096 "username" => "Lain"
1101 describe "create account by app / rate limit" do
1102 setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
1104 test "respects rate limit setting", %{conn: conn} do
1105 app_token = insert(:oauth_token, user: nil)
1109 |> put_req_header("authorization", "Bearer " <> app_token.token)
1110 |> Map.put(:remote_ip, {15, 15, 15, 15})
1111 |> put_req_header("content-type", "multipart/form-data")
1116 |> post("/api/v1/accounts", %{
1117 username: "#{i}lain",
1118 email: "#{i}lain@example.org",
1119 password: "PlzDontHackLain",
1124 "access_token" => token,
1125 "created_at" => _created_at,
1127 "token_type" => "Bearer"
1128 } = json_response(conn, 200)
1130 token_from_db = Repo.get_by(Token, token: token)
1131 assert token_from_db
1132 token_from_db = Repo.preload(token_from_db, :user)
1133 assert token_from_db.user
1135 assert token_from_db.user.confirmation_pending
1139 post(conn, "/api/v1/accounts", %{
1141 email: "6lain@example.org",
1142 password: "PlzDontHackLain",
1146 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
1150 describe "GET /api/v1/accounts/:id/lists - account_lists" do
1151 test "returns lists to which the account belongs" do
1152 %{user: user, conn: conn} = oauth_access(["read:lists"])
1153 other_user = insert(:user)
1154 assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user)
1155 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
1159 |> get("/api/v1/accounts/#{other_user.id}/lists")
1160 |> json_response(200)
1162 assert res == [%{"id" => to_string(list.id), "title" => "Test List"}]
1163 assert_schema(res, "ListsResponse", ApiSpec.spec())
1167 describe "verify_credentials" do
1168 test "verify_credentials" do
1169 %{user: user, conn: conn} = oauth_access(["read:accounts"])
1170 conn = get(conn, "/api/v1/accounts/verify_credentials")
1172 response = json_response(conn, 200)
1174 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
1175 assert response["pleroma"]["chat_token"]
1176 assert id == to_string(user.id)
1179 test "verify_credentials default scope unlisted" do
1180 user = insert(:user, default_scope: "unlisted")
1181 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1183 conn = get(conn, "/api/v1/accounts/verify_credentials")
1185 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
1186 assert id == to_string(user.id)
1189 test "locked accounts" do
1190 user = insert(:user, default_scope: "private")
1191 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1193 conn = get(conn, "/api/v1/accounts/verify_credentials")
1195 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1196 assert id == to_string(user.id)
1200 describe "user relationships" do
1201 setup do: oauth_access(["read:follows"])
1203 test "returns the relationships for the current user", %{user: user, conn: conn} do
1204 %{id: other_user_id} = other_user = insert(:user)
1205 {:ok, _user} = User.follow(user, other_user)
1207 assert [%{"id" => ^other_user_id}] =
1209 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1210 |> json_response(200)
1212 assert [%{"id" => ^other_user_id}] =
1214 |> get("/api/v1/accounts/relationships?id[]=#{other_user.id}")
1215 |> json_response(200)
1218 test "returns an empty list on a bad request", %{conn: conn} do
1219 conn = get(conn, "/api/v1/accounts/relationships", %{})
1221 assert [] = json_response(conn, 200)
1225 test "getting a list of mutes" do
1226 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1227 other_user = insert(:user)
1229 {:ok, _user_relationships} = User.mute(user, other_user)
1231 conn = get(conn, "/api/v1/mutes")
1233 other_user_id = to_string(other_user.id)
1234 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1235 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
1238 test "getting a list of blocks" do
1239 %{user: user, conn: conn} = oauth_access(["read:blocks"])
1240 other_user = insert(:user)
1242 {:ok, _user_relationship} = User.block(user, other_user)
1246 |> assign(:user, user)
1247 |> get("/api/v1/blocks")
1249 other_user_id = to_string(other_user.id)
1250 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1251 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())