1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 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
10 alias Pleroma.Tests.ObanHelpers
12 alias Pleroma.UserRelationship
13 alias Pleroma.Web.ActivityPub.ActivityPub
14 alias Pleroma.Web.ActivityPub.InternalFetchActor
15 alias Pleroma.Web.CommonAPI
16 alias Pleroma.Web.OAuth.Token
17 alias Pleroma.Web.Plugs.SetLocalePlug
19 import Pleroma.Factory
21 describe "account fetching" do
23 %User{id: user_id} = insert(:user)
25 assert %{"id" => ^user_id} =
27 |> get("/api/v1/accounts/#{user_id}")
28 |> json_response_and_validate_schema(200)
30 assert %{"error" => "Can't find user"} =
32 |> get("/api/v1/accounts/-1")
33 |> json_response_and_validate_schema(404)
36 test "relationship field" do
37 %{conn: conn, user: user} = oauth_access(["read"])
39 other_user = insert(:user)
43 |> get("/api/v1/accounts/#{other_user.id}")
44 |> json_response_and_validate_schema(200)
46 assert response["id"] == other_user.id
47 assert response["pleroma"]["relationship"] == %{}
49 assert %{"pleroma" => %{"relationship" => %{"following" => false, "followed_by" => false}}} =
51 |> get("/api/v1/accounts/#{other_user.id}?with_relationships=true")
52 |> json_response_and_validate_schema(200)
54 {:ok, _, %{id: other_id}} = User.follow(user, other_user)
58 "pleroma" => %{"relationship" => %{"following" => true, "followed_by" => false}}
61 |> get("/api/v1/accounts/#{other_id}?with_relationships=true")
62 |> json_response_and_validate_schema(200)
64 {:ok, _, _} = User.follow(other_user, user)
68 "pleroma" => %{"relationship" => %{"following" => true, "followed_by" => true}}
71 |> get("/api/v1/accounts/#{other_id}?with_relationships=true")
72 |> json_response_and_validate_schema(200)
75 test "works by nickname" do
78 assert %{"id" => _user_id} =
80 |> get("/api/v1/accounts/#{user.nickname}")
81 |> json_response_and_validate_schema(200)
84 test "works by nickname for remote users" do
85 clear_config([:instance, :limit_to_local_content], false)
87 user = insert(:user, nickname: "user@example.com", local: false)
89 assert %{"id" => _user_id} =
91 |> get("/api/v1/accounts/#{user.nickname}")
92 |> json_response_and_validate_schema(200)
95 test "respects limit_to_local_content == :all for remote user nicknames" do
96 clear_config([:instance, :limit_to_local_content], :all)
98 user = insert(:user, nickname: "user@example.com", local: false)
101 |> get("/api/v1/accounts/#{user.nickname}")
102 |> json_response_and_validate_schema(404)
105 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
106 clear_config([:instance, :limit_to_local_content], :unauthenticated)
108 user = insert(:user, nickname: "user@example.com", local: false)
109 reading_user = insert(:user)
113 |> get("/api/v1/accounts/#{user.nickname}")
115 assert json_response_and_validate_schema(conn, 404)
119 |> assign(:user, reading_user)
120 |> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"]))
121 |> get("/api/v1/accounts/#{user.nickname}")
123 assert %{"id" => id} = json_response_and_validate_schema(conn, 200)
127 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
128 # Need to set an old-style integer ID to reproduce the problem
129 # (these are no longer assigned to new accounts but were preserved
130 # for existing accounts during the migration to flakeIDs)
131 user_one = insert(:user, %{id: 1212})
132 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
136 |> get("/api/v1/accounts/#{user_one.id}")
137 |> json_response_and_validate_schema(:ok)
141 |> get("/api/v1/accounts/#{user_two.nickname}")
142 |> json_response_and_validate_schema(:ok)
146 |> get("/api/v1/accounts/#{user_two.id}")
147 |> json_response_and_validate_schema(:ok)
149 refute acc_one == acc_two
150 assert acc_two == acc_three
153 test "returns 404 when user is invisible", %{conn: conn} do
154 user = insert(:user, %{invisible: true})
156 assert %{"error" => "Can't find user"} =
158 |> get("/api/v1/accounts/#{user.nickname}")
159 |> json_response_and_validate_schema(404)
162 test "returns 404 for internal.fetch actor", %{conn: conn} do
163 %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor()
165 assert %{"error" => "Can't find user"} =
167 |> get("/api/v1/accounts/internal.fetch")
168 |> json_response_and_validate_schema(404)
171 test "returns 404 for deactivated user", %{conn: conn} do
172 user = insert(:user, is_active: false)
174 assert %{"error" => "Can't find user"} =
176 |> get("/api/v1/accounts/#{user.id}")
177 |> json_response_and_validate_schema(:not_found)
181 defp local_and_remote_users do
182 local = insert(:user)
183 remote = insert(:user, local: false)
184 {:ok, local: local, remote: remote}
187 describe "user fetching with restrict unauthenticated profiles for local and remote" do
188 setup do: local_and_remote_users()
190 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
192 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
194 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
195 assert %{"error" => "This API requires an authenticated user"} ==
197 |> get("/api/v1/accounts/#{local.id}")
198 |> json_response_and_validate_schema(:unauthorized)
200 assert %{"error" => "This API requires an authenticated user"} ==
202 |> get("/api/v1/accounts/#{remote.id}")
203 |> json_response_and_validate_schema(:unauthorized)
206 test "if user is authenticated", %{local: local, remote: remote} do
207 %{conn: conn} = oauth_access(["read"])
209 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
210 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
212 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
213 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
217 describe "user fetching with restrict unauthenticated profiles for local" do
218 setup do: local_and_remote_users()
220 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
222 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
223 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
225 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
226 "error" => "This API requires an authenticated user"
229 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
230 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
233 test "if user is authenticated", %{local: local, remote: remote} do
234 %{conn: conn} = oauth_access(["read"])
236 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
237 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
239 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
240 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
244 describe "user fetching with restrict unauthenticated profiles for remote" do
245 setup do: local_and_remote_users()
247 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
249 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
250 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
251 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
253 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
255 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
256 "error" => "This API requires an authenticated user"
260 test "if user is authenticated", %{local: local, remote: remote} do
261 %{conn: conn} = oauth_access(["read"])
263 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
264 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
266 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
267 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
271 describe "user timelines" do
272 setup do: oauth_access(["read:statuses"])
274 test "works with announces that are just addressed to public", %{conn: conn} do
275 user = insert(:user, ap_id: "https://honktest/u/test", local: false)
276 other_user = insert(:user)
278 {:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"})
282 "@context" => "https://www.w3.org/ns/activitystreams",
283 "actor" => "https://honktest/u/test",
284 "id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx",
285 "object" => post.data["object"],
286 "published" => "2019-06-25T19:33:58Z",
287 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
290 |> ActivityPub.persist(local: false)
294 |> get("/api/v1/accounts/#{user.id}/statuses")
295 |> json_response_and_validate_schema(200)
297 assert [%{"id" => id}] = resp
298 assert id == announce.id
301 test "deactivated user", %{conn: conn} do
302 user = insert(:user, is_active: false)
304 assert %{"error" => "Can't find user"} ==
306 |> get("/api/v1/accounts/#{user.id}/statuses")
307 |> json_response_and_validate_schema(:not_found)
310 test "returns 404 when user is invisible", %{conn: conn} do
311 user = insert(:user, %{invisible: true})
313 assert %{"error" => "Can't find user"} =
315 |> get("/api/v1/accounts/#{user.id}")
316 |> json_response_and_validate_schema(404)
319 test "respects blocks", %{user: user_one, conn: conn} do
320 user_two = insert(:user)
321 user_three = insert(:user)
323 User.block(user_one, user_two)
325 {:ok, activity} = CommonAPI.post(user_two, %{status: "User one sux0rz"})
326 {:ok, repeat} = CommonAPI.repeat(activity.id, user_three)
330 |> get("/api/v1/accounts/#{user_two.id}/statuses")
331 |> json_response_and_validate_schema(200)
333 assert [%{"id" => id}] = resp
334 assert id == activity.id
336 # Even a blocked user will deliver the full user timeline, there would be
337 # no point in looking at a blocked users timeline otherwise
340 |> get("/api/v1/accounts/#{user_two.id}/statuses")
341 |> json_response_and_validate_schema(200)
343 assert [%{"id" => id}] = resp
344 assert id == activity.id
346 # Third user's timeline includes the repeat when viewed by unauthenticated user
349 |> get("/api/v1/accounts/#{user_three.id}/statuses")
350 |> json_response_and_validate_schema(200)
352 assert [%{"id" => id}] = resp
353 assert id == repeat.id
355 # When viewing a third user's timeline, the blocked users' statuses will NOT be shown
356 resp = get(conn, "/api/v1/accounts/#{user_three.id}/statuses")
358 assert [] == json_response_and_validate_schema(resp, 200)
361 test "gets users statuses", %{conn: conn} do
362 user_one = insert(:user)
363 user_two = insert(:user)
364 user_three = insert(:user)
366 {:ok, _user_three, _user_one} = User.follow(user_three, user_one)
368 {:ok, activity} = CommonAPI.post(user_one, %{status: "HI!!!"})
370 {:ok, direct_activity} =
371 CommonAPI.post(user_one, %{
372 status: "Hi, @#{user_two.nickname}.",
376 {:ok, private_activity} =
377 CommonAPI.post(user_one, %{status: "private", visibility: "private"})
382 |> get("/api/v1/accounts/#{user_one.id}/statuses")
383 |> json_response_and_validate_schema(200)
385 assert [%{"id" => id}] = resp
386 assert id == to_string(activity.id)
390 |> assign(:user, user_two)
391 |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"]))
392 |> get("/api/v1/accounts/#{user_one.id}/statuses")
393 |> json_response_and_validate_schema(200)
395 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
396 assert id_one == to_string(direct_activity.id)
397 assert id_two == to_string(activity.id)
401 |> assign(:user, user_three)
402 |> assign(:token, insert(:oauth_token, user: user_three, scopes: ["read:statuses"]))
403 |> get("/api/v1/accounts/#{user_one.id}/statuses")
404 |> json_response_and_validate_schema(200)
406 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
407 assert id_one == to_string(private_activity.id)
408 assert id_two == to_string(activity.id)
411 test "gets local-only statuses for authenticated users", %{user: _user, conn: conn} do
412 user_one = insert(:user)
414 {:ok, activity} = CommonAPI.post(user_one, %{status: "HI!!!", visibility: "local"})
418 |> get("/api/v1/accounts/#{user_one.id}/statuses")
419 |> json_response_and_validate_schema(200)
421 assert [%{"id" => id}] = resp
422 assert id == to_string(activity.id)
425 test "gets an users media, excludes reblogs", %{conn: conn} do
426 note = insert(:note_activity)
427 user = User.get_cached_by_ap_id(note.data["actor"])
428 other_user = insert(:user)
431 content_type: "image/jpeg",
432 path: Path.absname("test/fixtures/image.jpg"),
433 filename: "an_image.jpg"
436 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
438 {:ok, %{id: image_post_id}} = CommonAPI.post(user, %{status: "cofe", media_ids: [media_id]})
440 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: other_user.ap_id)
442 {:ok, %{id: other_image_post_id}} =
443 CommonAPI.post(other_user, %{status: "cofe2", media_ids: [media_id]})
445 {:ok, _announce} = CommonAPI.repeat(other_image_post_id, user)
447 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true")
449 assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
451 conn = get(build_conn(), "/api/v1/accounts/#{user.id}/statuses?only_media=1")
453 assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
456 test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
457 {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"})
458 {:ok, _} = CommonAPI.repeat(post_id, user)
460 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
461 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
463 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=1")
464 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
467 test "filters user's statuses by a hashtag", %{user: user, conn: conn} do
468 {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "#hashtag"})
469 {:ok, _post} = CommonAPI.post(user, %{status: "hashtag"})
471 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?tagged=hashtag")
472 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
475 test "the user views their own timelines and excludes direct messages", %{
479 {:ok, %{id: public_activity_id}} =
480 CommonAPI.post(user, %{status: ".", visibility: "public"})
482 {:ok, _direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
484 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_visibilities[]=direct")
485 assert [%{"id" => ^public_activity_id}] = json_response_and_validate_schema(conn, 200)
488 test "muted reactions", %{user: user, conn: conn} do
489 user2 = insert(:user)
490 User.mute(user, user2)
491 {:ok, activity} = CommonAPI.post(user, %{status: "."})
492 {:ok, _} = CommonAPI.react_with_emoji(activity.id, user2, "🎅")
496 |> get("/api/v1/accounts/#{user.id}/statuses")
497 |> json_response_and_validate_schema(200)
502 "emoji_reactions" => []
509 |> get("/api/v1/accounts/#{user.id}/statuses?with_muted=true")
510 |> json_response_and_validate_schema(200)
515 "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}]
521 test "paginates a user's statuses", %{user: user, conn: conn} do
522 {:ok, post_1} = CommonAPI.post(user, %{status: "first post"})
523 {:ok, post_2} = CommonAPI.post(user, %{status: "second post"})
525 response_1 = get(conn, "/api/v1/accounts/#{user.id}/statuses?limit=1")
526 assert [res] = json_response_and_validate_schema(response_1, 200)
527 assert res["id"] == post_2.id
529 response_2 = get(conn, "/api/v1/accounts/#{user.id}/statuses?limit=1&max_id=#{res["id"]}")
530 assert [res] = json_response_and_validate_schema(response_2, 200)
531 assert res["id"] == post_1.id
533 refute response_1 == response_2
537 defp local_and_remote_activities(%{local: local, remote: remote}) do
538 insert(:note_activity, user: local)
539 insert(:note_activity, user: remote, local: false)
544 describe "statuses with restrict unauthenticated profiles for local and remote" do
545 setup do: local_and_remote_users()
546 setup :local_and_remote_activities
548 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
550 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
552 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
553 assert %{"error" => "This API requires an authenticated user"} ==
555 |> get("/api/v1/accounts/#{local.id}/statuses")
556 |> json_response_and_validate_schema(:unauthorized)
558 assert %{"error" => "This API requires an authenticated user"} ==
560 |> get("/api/v1/accounts/#{remote.id}/statuses")
561 |> json_response_and_validate_schema(:unauthorized)
564 test "if user is authenticated", %{local: local, remote: remote} do
565 %{conn: conn} = oauth_access(["read"])
567 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
568 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
570 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
571 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
575 describe "statuses with restrict unauthenticated profiles for local" do
576 setup do: local_and_remote_users()
577 setup :local_and_remote_activities
579 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
581 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
582 assert %{"error" => "This API requires an authenticated user"} ==
584 |> get("/api/v1/accounts/#{local.id}/statuses")
585 |> json_response_and_validate_schema(:unauthorized)
587 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
588 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
591 test "if user is authenticated", %{local: local, remote: remote} do
592 %{conn: conn} = oauth_access(["read"])
594 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
595 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
597 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
598 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
602 describe "statuses with restrict unauthenticated profiles for remote" do
603 setup do: local_and_remote_users()
604 setup :local_and_remote_activities
606 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
608 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
609 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
610 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
612 assert %{"error" => "This API requires an authenticated user"} ==
614 |> get("/api/v1/accounts/#{remote.id}/statuses")
615 |> json_response_and_validate_schema(:unauthorized)
618 test "if user is authenticated", %{local: local, remote: remote} do
619 %{conn: conn} = oauth_access(["read"])
621 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
622 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
624 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
625 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
629 describe "followers" do
630 setup do: oauth_access(["read:accounts"])
632 test "getting followers", %{user: user, conn: conn} do
633 other_user = insert(:user)
634 {:ok, %{id: user_id}, other_user} = User.follow(user, other_user)
636 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
638 assert [%{"id" => ^user_id}] = json_response_and_validate_schema(conn, 200)
641 test "following with relationship", %{conn: conn, user: user} do
642 other_user = insert(:user)
643 {:ok, %{id: id}, _} = User.follow(other_user, user)
651 "following" => false,
652 "followed_by" => true
658 |> get("/api/v1/accounts/#{user.id}/followers?with_relationships=true")
659 |> json_response_and_validate_schema(200)
661 {:ok, _, _} = User.follow(user, other_user)
670 "followed_by" => true
676 |> get("/api/v1/accounts/#{user.id}/followers?with_relationships=true")
677 |> json_response_and_validate_schema(200)
680 test "getting followers, hide_followers", %{user: user, conn: conn} do
681 other_user = insert(:user, hide_followers: true)
682 {:ok, _user, _other_user} = User.follow(user, other_user)
684 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
686 assert [] == json_response_and_validate_schema(conn, 200)
689 test "getting followers, hide_followers, same user requesting" do
691 other_user = insert(:user, hide_followers: true)
692 {:ok, _user, _other_user} = User.follow(user, other_user)
696 |> assign(:user, other_user)
697 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
698 |> get("/api/v1/accounts/#{other_user.id}/followers")
700 refute [] == json_response_and_validate_schema(conn, 200)
703 test "getting followers, pagination", %{user: user, conn: conn} do
704 {:ok, %User{id: follower1_id}, _user} = :user |> insert() |> User.follow(user)
705 {:ok, %User{id: follower2_id}, _user} = :user |> insert() |> User.follow(user)
706 {:ok, %User{id: follower3_id}, _user} = :user |> insert() |> User.follow(user)
708 assert [%{"id" => ^follower3_id}, %{"id" => ^follower2_id}] =
710 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1_id}")
711 |> json_response_and_validate_schema(200)
713 assert [%{"id" => ^follower2_id}, %{"id" => ^follower1_id}] =
715 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3_id}")
716 |> json_response_and_validate_schema(200)
718 assert [%{"id" => ^follower2_id}, %{"id" => ^follower1_id}] =
721 "/api/v1/accounts/#{user.id}/followers?id=#{user.id}&limit=20&max_id=#{follower3_id}"
723 |> json_response_and_validate_schema(200)
725 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3_id}")
727 assert [%{"id" => ^follower2_id}] = json_response_and_validate_schema(res_conn, 200)
729 assert [link_header] = get_resp_header(res_conn, "link")
730 assert link_header =~ ~r/min_id=#{follower2_id}/
731 assert link_header =~ ~r/max_id=#{follower2_id}/
735 describe "following" do
736 setup do: oauth_access(["read:accounts"])
738 test "getting following", %{user: user, conn: conn} do
739 other_user = insert(:user)
740 {:ok, user, other_user} = User.follow(user, other_user)
742 conn = get(conn, "/api/v1/accounts/#{user.id}/following")
744 assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200)
745 assert id == to_string(other_user.id)
748 test "following with relationship", %{conn: conn, user: user} do
749 other_user = insert(:user)
750 {:ok, user, other_user} = User.follow(user, other_user)
752 conn = get(conn, "/api/v1/accounts/#{user.id}/following?with_relationships=true")
760 "relationship" => %{"id" => ^id, "following" => true, "followed_by" => false}
763 ] = json_response_and_validate_schema(conn, 200)
766 test "getting following, hide_follows, other user requesting" do
767 user = insert(:user, hide_follows: true)
768 other_user = insert(:user)
769 {:ok, user, other_user} = User.follow(user, other_user)
773 |> assign(:user, other_user)
774 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
775 |> get("/api/v1/accounts/#{user.id}/following")
777 assert [] == json_response_and_validate_schema(conn, 200)
780 test "getting following, hide_follows, same user requesting" do
781 user = insert(:user, hide_follows: true)
782 other_user = insert(:user)
783 {:ok, user, _other_user} = User.follow(user, other_user)
787 |> assign(:user, user)
788 |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"]))
789 |> get("/api/v1/accounts/#{user.id}/following")
791 refute [] == json_response_and_validate_schema(conn, 200)
794 test "getting following, pagination", %{user: user, conn: conn} do
795 following1 = insert(:user)
796 following2 = insert(:user)
797 following3 = insert(:user)
798 {:ok, _, _} = User.follow(user, following1)
799 {:ok, _, _} = User.follow(user, following2)
800 {:ok, _, _} = User.follow(user, following3)
802 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
804 assert [%{"id" => id3}, %{"id" => id2}] = json_response_and_validate_schema(res_conn, 200)
805 assert id3 == following3.id
806 assert id2 == following2.id
808 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
810 assert [%{"id" => id2}, %{"id" => id1}] = json_response_and_validate_schema(res_conn, 200)
811 assert id2 == following2.id
812 assert id1 == following1.id
817 "/api/v1/accounts/#{user.id}/following?id=#{user.id}&limit=20&max_id=#{following3.id}"
820 assert [%{"id" => id2}, %{"id" => id1}] = json_response_and_validate_schema(res_conn, 200)
821 assert id2 == following2.id
822 assert id1 == following1.id
825 get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
827 assert [%{"id" => id2}] = json_response_and_validate_schema(res_conn, 200)
828 assert id2 == following2.id
830 assert [link_header] = get_resp_header(res_conn, "link")
831 assert link_header =~ ~r/min_id=#{following2.id}/
832 assert link_header =~ ~r/max_id=#{following2.id}/
836 describe "follow/unfollow" do
837 setup do: oauth_access(["follow"])
839 test "following / unfollowing a user", %{conn: conn} do
840 %{id: other_user_id, nickname: other_user_nickname} = insert(:user)
842 assert %{"id" => _id, "following" => true} =
844 |> post("/api/v1/accounts/#{other_user_id}/follow")
845 |> json_response_and_validate_schema(200)
847 assert %{"id" => _id, "following" => false} =
849 |> post("/api/v1/accounts/#{other_user_id}/unfollow")
850 |> json_response_and_validate_schema(200)
852 assert %{"id" => ^other_user_id} =
854 |> put_req_header("content-type", "application/json")
855 |> post("/api/v1/follows", %{"uri" => other_user_nickname})
856 |> json_response_and_validate_schema(200)
859 test "cancelling follow request", %{conn: conn} do
860 %{id: other_user_id} = insert(:user, %{is_locked: true})
862 assert %{"id" => ^other_user_id, "following" => false, "requested" => true} =
864 |> post("/api/v1/accounts/#{other_user_id}/follow")
865 |> json_response_and_validate_schema(:ok)
867 assert %{"id" => ^other_user_id, "following" => false, "requested" => false} =
869 |> post("/api/v1/accounts/#{other_user_id}/unfollow")
870 |> json_response_and_validate_schema(:ok)
873 test "following without reblogs" do
874 %{conn: conn} = oauth_access(["follow", "read:statuses"])
875 followed = insert(:user)
876 other_user = insert(:user)
880 |> put_req_header("content-type", "application/json")
881 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false})
883 assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200)
885 {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
886 {:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)
890 |> get("/api/v1/timelines/home")
891 |> json_response_and_validate_schema(200)
893 assert %{"showing_reblogs" => true} =
895 |> put_req_header("content-type", "application/json")
896 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: true})
897 |> json_response_and_validate_schema(200)
899 assert %{"showing_reblogs" => true} =
901 |> put_req_header("content-type", "application/json")
902 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: "1"})
903 |> json_response_and_validate_schema(200)
905 assert [%{"id" => ^reblog_id}] =
907 |> get("/api/v1/timelines/home")
908 |> json_response_and_validate_schema(200)
911 test "following with reblogs" do
912 %{conn: conn} = oauth_access(["follow", "read:statuses"])
913 followed = insert(:user)
914 other_user = insert(:user)
916 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow")
918 assert %{"showing_reblogs" => true} = json_response_and_validate_schema(ret_conn, 200)
920 {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
921 {:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)
923 assert [%{"id" => ^reblog_id}] =
925 |> get("/api/v1/timelines/home")
926 |> json_response_and_validate_schema(200)
928 assert %{"showing_reblogs" => false} =
930 |> put_req_header("content-type", "application/json")
931 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false})
932 |> json_response_and_validate_schema(200)
934 assert %{"showing_reblogs" => false} =
936 |> put_req_header("content-type", "application/json")
937 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: "0"})
938 |> json_response_and_validate_schema(200)
942 |> get("/api/v1/timelines/home")
943 |> json_response_and_validate_schema(200)
946 test "following with subscription and unsubscribing" do
947 %{conn: conn} = oauth_access(["follow"])
948 followed = insert(:user)
950 assert %{"subscribing" => true} =
952 |> put_req_header("content-type", "application/json")
953 |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: true})
954 |> json_response_and_validate_schema(200)
956 assert %{"subscribing" => true} =
958 |> put_req_header("content-type", "application/json")
959 |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: "1"})
960 |> json_response_and_validate_schema(200)
962 assert %{"subscribing" => false} =
964 |> put_req_header("content-type", "application/json")
965 |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: false})
966 |> json_response_and_validate_schema(200)
969 test "following / unfollowing errors", %{user: user, conn: conn} do
971 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
973 assert %{"error" => "Can not follow yourself"} =
974 json_response_and_validate_schema(conn_res, 400)
977 user = User.get_cached_by_id(user.id)
978 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
980 assert %{"error" => "Can not unfollow yourself"} =
981 json_response_and_validate_schema(conn_res, 400)
983 # self follow via uri
984 user = User.get_cached_by_id(user.id)
986 assert %{"error" => "Can not follow yourself"} =
988 |> put_req_header("content-type", "multipart/form-data")
989 |> post("/api/v1/follows", %{"uri" => user.nickname})
990 |> json_response_and_validate_schema(400)
992 # follow non existing user
993 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
994 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
996 # follow non existing user via uri
999 |> put_req_header("content-type", "multipart/form-data")
1000 |> post("/api/v1/follows", %{"uri" => "doesntexist"})
1002 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
1004 # unfollow non existing user
1005 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1006 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
1010 describe "mute/unmute" do
1011 setup do: oauth_access(["write:mutes"])
1013 test "with notifications", %{conn: conn} do
1014 other_user = insert(:user)
1016 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} =
1018 |> post("/api/v1/accounts/#{other_user.id}/mute")
1019 |> json_response_and_validate_schema(200)
1021 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
1023 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
1024 json_response_and_validate_schema(conn, 200)
1027 test "without notifications", %{conn: conn} do
1028 other_user = insert(:user)
1032 |> put_req_header("content-type", "multipart/form-data")
1033 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
1035 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} =
1036 json_response_and_validate_schema(ret_conn, 200)
1038 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
1040 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
1041 json_response_and_validate_schema(conn, 200)
1044 test "expiring", %{conn: conn, user: user} do
1045 other_user = insert(:user)
1049 |> put_req_header("content-type", "multipart/form-data")
1050 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"duration" => "86400"})
1052 assert %{"id" => _id, "muting" => true} = json_response_and_validate_schema(conn, 200)
1054 mute_expires_at = UserRelationship.get_mute_expire_date(user, other_user)
1056 assert DateTime.diff(
1058 DateTime.utc_now() |> DateTime.add(24 * 60 * 60)
1062 test "falls back to expires_in", %{conn: conn, user: user} do
1063 other_user = insert(:user)
1066 |> put_req_header("content-type", "multipart/form-data")
1067 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"expires_in" => "86400"})
1068 |> json_response_and_validate_schema(200)
1070 mute_expires_at = UserRelationship.get_mute_expire_date(user, other_user)
1072 assert DateTime.diff(
1074 DateTime.utc_now() |> DateTime.add(24 * 60 * 60)
1079 describe "pinned statuses" do
1081 user = insert(:user)
1082 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
1083 %{conn: conn} = oauth_access(["read:statuses"], user: user)
1085 [conn: conn, user: user, activity: activity]
1088 test "returns pinned statuses", %{conn: conn, user: user, activity: %{id: activity_id}} do
1089 {:ok, _} = CommonAPI.pin(activity_id, user)
1091 assert [%{"id" => ^activity_id, "pinned" => true}] =
1093 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1094 |> json_response_and_validate_schema(200)
1098 test "view pinned private statuses" do
1099 user = insert(:user)
1100 reader = insert(:user)
1102 # Create a private status and pin it
1103 {:ok, %{id: activity_id} = activity} =
1104 CommonAPI.post(user, %{status: "psst", visibility: "private"})
1106 %{data: %{"id" => object_ap_id}} = Object.normalize(activity)
1107 {:ok, _} = User.add_pinned_object_id(user, object_ap_id)
1109 %{conn: conn} = oauth_access(["read:statuses"], user: reader)
1111 # A non-follower can't see the pinned status
1114 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1115 |> json_response_and_validate_schema(200)
1117 # Follow the user, then the pinned status can be seen
1118 CommonAPI.follow(reader, user)
1119 ObanHelpers.perform_all()
1121 assert [%{"id" => ^activity_id, "pinned" => true}] =
1123 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1124 |> json_response_and_validate_schema(200)
1127 test "blocking / unblocking a user" do
1128 %{conn: conn} = oauth_access(["follow"])
1129 other_user = insert(:user)
1131 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block")
1133 assert %{"id" => _id, "blocking" => true} = json_response_and_validate_schema(ret_conn, 200)
1135 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock")
1137 assert %{"id" => _id, "blocking" => false} = json_response_and_validate_schema(conn, 200)
1140 describe "create account by app" do
1144 email: "lain@example.org",
1145 password: "PlzDontHackLain",
1149 [valid_params: valid_params]
1152 test "registers and logs in without :account_activation_required / :account_approval_required",
1154 clear_config([:instance, :account_activation_required], false)
1155 clear_config([:instance, :account_approval_required], false)
1159 |> put_req_header("content-type", "application/json")
1160 |> post("/api/v1/apps", %{
1161 client_name: "client_name",
1162 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1163 scopes: "read, write, follow"
1167 "client_id" => client_id,
1168 "client_secret" => client_secret,
1170 "name" => "client_name",
1171 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1174 } = json_response_and_validate_schema(conn, 200)
1177 post(conn, "/oauth/token", %{
1178 grant_type: "client_credentials",
1179 client_id: client_id,
1180 client_secret: client_secret
1183 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1184 json_response(conn, 200)
1187 token_from_db = Repo.get_by(Token, token: token)
1188 assert token_from_db
1190 assert scope == "read write follow"
1192 clear_config([User, :email_blacklist], ["example.org"])
1196 email: "lain@example.org",
1197 password: "PlzDontHackLain",
1204 |> put_req_header("content-type", "multipart/form-data")
1205 |> put_req_header("authorization", "Bearer " <> token)
1206 |> post("/api/v1/accounts", params)
1208 assert %{"error" => "{\"email\":[\"Invalid email\"]}"} =
1209 json_response_and_validate_schema(conn, 400)
1211 clear_config([User, :email_blacklist], [])
1215 |> put_req_header("content-type", "multipart/form-data")
1216 |> put_req_header("authorization", "Bearer " <> token)
1217 |> post("/api/v1/accounts", params)
1220 "access_token" => token,
1221 "created_at" => _created_at,
1223 "token_type" => "Bearer"
1224 } = json_response_and_validate_schema(conn, 200)
1226 token_from_db = Repo.get_by(Token, token: token)
1227 assert token_from_db
1228 user = Repo.preload(token_from_db, :user).user
1231 assert user.is_confirmed
1232 assert user.is_approved
1235 test "registers but does not log in with :account_activation_required", %{conn: conn} do
1236 clear_config([:instance, :account_activation_required], true)
1237 clear_config([:instance, :account_approval_required], false)
1241 |> put_req_header("content-type", "application/json")
1242 |> post("/api/v1/apps", %{
1243 client_name: "client_name",
1244 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1245 scopes: "read, write, follow"
1249 "client_id" => client_id,
1250 "client_secret" => client_secret,
1252 "name" => "client_name",
1253 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1256 } = json_response_and_validate_schema(conn, 200)
1259 post(conn, "/oauth/token", %{
1260 grant_type: "client_credentials",
1261 client_id: client_id,
1262 client_secret: client_secret
1265 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1266 json_response(conn, 200)
1269 token_from_db = Repo.get_by(Token, token: token)
1270 assert token_from_db
1272 assert scope == "read write follow"
1276 |> put_req_header("content-type", "multipart/form-data")
1277 |> put_req_header("authorization", "Bearer " <> token)
1278 |> post("/api/v1/accounts", %{
1280 email: "lain@example.org",
1281 password: "PlzDontHackLain",
1286 response = json_response_and_validate_schema(conn, 200)
1287 assert %{"identifier" => "missing_confirmed_email"} = response
1288 refute response["access_token"]
1289 refute response["token_type"]
1291 user = Repo.get_by(User, email: "lain@example.org")
1292 refute user.is_confirmed
1295 test "registers but does not log in with :account_approval_required", %{conn: conn} do
1296 clear_config([:instance, :account_approval_required], true)
1297 clear_config([:instance, :account_activation_required], false)
1301 |> put_req_header("content-type", "application/json")
1302 |> post("/api/v1/apps", %{
1303 client_name: "client_name",
1304 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1305 scopes: "read, write, follow"
1309 "client_id" => client_id,
1310 "client_secret" => client_secret,
1312 "name" => "client_name",
1313 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1316 } = json_response_and_validate_schema(conn, 200)
1319 post(conn, "/oauth/token", %{
1320 grant_type: "client_credentials",
1321 client_id: client_id,
1322 client_secret: client_secret
1325 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1326 json_response(conn, 200)
1329 token_from_db = Repo.get_by(Token, token: token)
1330 assert token_from_db
1332 assert scope == "read write follow"
1336 |> put_req_header("content-type", "multipart/form-data")
1337 |> put_req_header("authorization", "Bearer " <> token)
1338 |> post("/api/v1/accounts", %{
1340 email: "lain@example.org",
1341 password: "PlzDontHackLain",
1344 reason: "I'm a cool dude, bro"
1347 response = json_response_and_validate_schema(conn, 200)
1348 assert %{"identifier" => "awaiting_approval"} = response
1349 refute response["access_token"]
1350 refute response["token_type"]
1352 user = Repo.get_by(User, email: "lain@example.org")
1354 refute user.is_approved
1355 assert user.registration_reason == "I'm a cool dude, bro"
1358 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
1359 _user = insert(:user, email: "lain@example.org")
1360 app_token = insert(:oauth_token, user: nil)
1364 |> put_req_header("authorization", "Bearer " <> app_token.token)
1365 |> put_req_header("content-type", "application/json")
1366 |> post("/api/v1/accounts", valid_params)
1368 assert json_response_and_validate_schema(res, 400) == %{
1369 "error" => "{\"email\":[\"has already been taken\"]}"
1373 test "returns bad_request if missing required params", %{
1375 valid_params: valid_params
1377 app_token = insert(:oauth_token, user: nil)
1381 |> put_req_header("authorization", "Bearer " <> app_token.token)
1382 |> put_req_header("content-type", "application/json")
1384 res = post(conn, "/api/v1/accounts", valid_params)
1385 assert json_response_and_validate_schema(res, 200)
1387 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
1388 |> Stream.zip(Map.delete(valid_params, :email))
1389 |> Enum.each(fn {ip, {attr, _}} ->
1392 |> Map.put(:remote_ip, ip)
1393 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
1394 |> json_response_and_validate_schema(400)
1397 "error" => "Missing field: #{attr}.",
1400 "message" => "Missing field: #{attr}",
1401 "source" => %{"pointer" => "/#{attr}"},
1402 "title" => "Invalid value"
1409 test "returns bad_request if missing email params when :account_activation_required is enabled",
1410 %{conn: conn, valid_params: valid_params} do
1411 clear_config([:instance, :account_activation_required], true)
1413 app_token = insert(:oauth_token, user: nil)
1417 |> put_req_header("authorization", "Bearer " <> app_token.token)
1418 |> put_req_header("content-type", "application/json")
1422 |> Map.put(:remote_ip, {127, 0, 0, 5})
1423 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
1425 assert json_response_and_validate_schema(res, 400) ==
1426 %{"error" => "Missing parameter: email"}
1430 |> Map.put(:remote_ip, {127, 0, 0, 6})
1431 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
1433 assert json_response_and_validate_schema(res, 400) == %{
1434 "error" => "{\"email\":[\"can't be blank\"]}"
1438 test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
1439 app_token = insert(:oauth_token, user: nil)
1440 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1444 |> put_req_header("content-type", "application/json")
1445 |> Map.put(:remote_ip, {127, 0, 0, 7})
1446 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
1448 assert json_response_and_validate_schema(res, 200)
1451 test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
1452 app_token = insert(:oauth_token, user: nil)
1453 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1457 |> put_req_header("content-type", "application/json")
1458 |> Map.put(:remote_ip, {127, 0, 0, 8})
1459 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
1461 assert json_response_and_validate_schema(res, 200)
1464 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
1467 |> put_req_header("authorization", "Bearer " <> "invalid-token")
1468 |> put_req_header("content-type", "multipart/form-data")
1469 |> post("/api/v1/accounts", valid_params)
1471 assert json_response_and_validate_schema(res, 403) == %{"error" => "Invalid credentials"}
1474 test "registration from trusted app" do
1475 clear_config([Pleroma.Captcha, :enabled], true)
1476 app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
1480 |> post("/oauth/token", %{
1481 "grant_type" => "client_credentials",
1482 "client_id" => app.client_id,
1483 "client_secret" => app.client_secret
1486 assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
1490 |> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
1491 |> put_req_header("content-type", "multipart/form-data")
1492 |> post("/api/v1/accounts", %{
1493 nickname: "nickanme",
1495 email: "email@example.com",
1498 password: "some_password",
1499 confirm: "some_password"
1501 |> json_response_and_validate_schema(200)
1504 "access_token" => access_token,
1506 "scope" => "read write follow push",
1507 "token_type" => "Bearer"
1512 |> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
1513 |> get("/api/v1/accounts/verify_credentials")
1514 |> json_response_and_validate_schema(200)
1519 "display_name" => "Lain",
1520 "follow_requests_count" => 0,
1521 "followers_count" => 0,
1522 "following_count" => 0,
1529 "actor_type" => "Person",
1530 "discoverable" => false,
1531 "no_rich_text" => false,
1534 "privacy" => "public",
1535 "sensitive" => false
1537 "statuses_count" => 0,
1538 "username" => "Lain"
1543 describe "create account by app / rate limit" do
1544 setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
1546 test "respects rate limit setting", %{conn: conn} do
1547 app_token = insert(:oauth_token, user: nil)
1551 |> put_req_header("authorization", "Bearer " <> app_token.token)
1552 |> Map.put(:remote_ip, {15, 15, 15, 15})
1553 |> put_req_header("content-type", "multipart/form-data")
1558 |> post("/api/v1/accounts", %{
1559 username: "#{i}lain",
1560 email: "#{i}lain@example.org",
1561 password: "PlzDontHackLain",
1566 "access_token" => token,
1567 "created_at" => _created_at,
1569 "token_type" => "Bearer"
1570 } = json_response_and_validate_schema(conn, 200)
1572 token_from_db = Repo.get_by(Token, token: token)
1573 assert token_from_db
1574 token_from_db = Repo.preload(token_from_db, :user)
1575 assert token_from_db.user
1579 post(conn, "/api/v1/accounts", %{
1581 email: "6lain@example.org",
1582 password: "PlzDontHackLain",
1586 assert json_response_and_validate_schema(conn, :too_many_requests) == %{
1587 "error" => "Throttled"
1592 describe "create account with enabled captcha" do
1593 setup %{conn: conn} do
1594 app_token = insert(:oauth_token, user: nil)
1598 |> put_req_header("authorization", "Bearer " <> app_token.token)
1599 |> put_req_header("content-type", "multipart/form-data")
1604 setup do: clear_config([Pleroma.Captcha, :enabled], true)
1606 test "creates an account and returns 200 if captcha is valid", %{conn: conn} do
1607 %{token: token, answer_data: answer_data} = Pleroma.Captcha.new()
1611 email: "lain@example.org",
1612 password: "PlzDontHackLain",
1614 captcha_solution: Pleroma.Captcha.Mock.solution(),
1615 captcha_token: token,
1616 captcha_answer_data: answer_data
1620 "access_token" => access_token,
1623 "token_type" => "Bearer"
1626 |> post("/api/v1/accounts", params)
1627 |> json_response_and_validate_schema(:ok)
1629 assert Token |> Repo.get_by(token: access_token) |> Repo.preload(:user) |> Map.get(:user)
1632 test "returns 400 if any captcha field is not provided", %{conn: conn} do
1633 captcha_fields = [:captcha_solution, :captcha_token, :captcha_answer_data]
1637 email: "lain@example.org",
1638 password: "PlzDontHackLain",
1640 captcha_solution: "xx",
1641 captcha_token: "xx",
1642 captcha_answer_data: "xx"
1645 for field <- captcha_fields do
1647 "error" => "{\"captcha\":[\"Invalid CAPTCHA (Missing parameter: #{field})\"]}"
1652 |> post("/api/v1/accounts", Map.delete(valid_params, field))
1653 |> json_response_and_validate_schema(:bad_request)
1657 test "returns an error if captcha is invalid", %{conn: conn} do
1660 email: "lain@example.org",
1661 password: "PlzDontHackLain",
1663 captcha_solution: "cofe",
1664 captcha_token: "cofe",
1665 captcha_answer_data: "cofe"
1668 assert %{"error" => "{\"captcha\":[\"Invalid answer data\"]}"} ==
1670 |> post("/api/v1/accounts", params)
1671 |> json_response_and_validate_schema(:bad_request)
1675 describe "create account with required birth date" do
1676 setup %{conn: conn} do
1677 clear_config([:instance, :birthday_required], true)
1678 clear_config([:instance, :birthday_min_age], 18 * 365)
1680 app_token = insert(:oauth_token, user: nil)
1684 |> put_req_header("authorization", "Bearer " <> app_token.token)
1685 |> put_req_header("content-type", "multipart/form-data")
1690 test "creates an account if provided valid birth date", %{conn: conn} do
1693 |> Date.add(-19 * 365)
1697 username: "mkljczk",
1698 email: "mkljczk@example.org",
1706 |> post("/api/v1/accounts", params)
1708 assert json_response_and_validate_schema(res, 200)
1711 test "returns an error if missing birth date", %{conn: conn} do
1713 username: "mkljczk",
1714 email: "mkljczk@example.org",
1721 |> post("/api/v1/accounts", params)
1723 assert json_response_and_validate_schema(res, 400) == %{
1724 "error" => "{\"birthday\":[\"can't be blank\"]}"
1729 describe "create account with language" do
1730 setup %{conn: conn} do
1731 app_token = insert(:oauth_token, user: nil)
1735 |> put_req_header("authorization", "Bearer " <> app_token.token)
1736 |> put_req_header("content-type", "multipart/form-data")
1737 |> put_req_cookie(SetLocalePlug.frontend_language_cookie_name(), "zh-Hans")
1738 |> SetLocalePlug.call([])
1743 test "creates an account with language parameter", %{conn: conn} do
1746 email: "foo@example.org",
1754 |> post("/api/v1/accounts", params)
1756 assert json_response_and_validate_schema(res, 200)
1758 assert %{language: "ru"} = Pleroma.User.get_by_nickname("foo")
1761 test "language parameter should be normalized", %{conn: conn} do
1764 email: "foo@example.org",
1772 |> post("/api/v1/accounts", params)
1774 assert json_response_and_validate_schema(res, 200)
1776 assert %{language: "ru_RU"} = Pleroma.User.get_by_nickname("foo")
1779 test "createing an account without language parameter should fallback to cookie/header language",
1783 email: "foo2@example.org",
1790 |> post("/api/v1/accounts", params)
1792 assert json_response_and_validate_schema(res, 200)
1794 assert %{language: "zh_Hans"} = Pleroma.User.get_by_nickname("foo2")
1798 describe "GET /api/v1/accounts/:id/lists - account_lists" do
1799 test "returns lists to which the account belongs" do
1800 %{user: user, conn: conn} = oauth_access(["read:lists"])
1801 other_user = insert(:user)
1802 assert {:ok, %Pleroma.List{id: _list_id} = list} = Pleroma.List.create("Test List", user)
1803 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
1805 assert [%{"id" => _list_id, "title" => "Test List"}] =
1807 |> get("/api/v1/accounts/#{other_user.id}/lists")
1808 |> json_response_and_validate_schema(200)
1812 describe "verify_credentials" do
1813 test "verify_credentials" do
1814 %{user: user, conn: conn} = oauth_access(["read:accounts"])
1816 [notification | _] =
1817 insert_list(7, :notification, user: user, activity: insert(:note_activity))
1819 Pleroma.Notification.set_read_up_to(user, notification.id)
1820 conn = get(conn, "/api/v1/accounts/verify_credentials")
1822 response = json_response_and_validate_schema(conn, 200)
1824 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
1825 assert response["pleroma"]["chat_token"]
1826 assert response["pleroma"]["unread_notifications_count"] == 6
1827 assert id == to_string(user.id)
1830 test "verify_credentials default scope unlisted" do
1831 user = insert(:user, default_scope: "unlisted")
1832 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1834 conn = get(conn, "/api/v1/accounts/verify_credentials")
1836 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} =
1837 json_response_and_validate_schema(conn, 200)
1839 assert id == to_string(user.id)
1842 test "locked accounts" do
1843 user = insert(:user, default_scope: "private")
1844 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1846 conn = get(conn, "/api/v1/accounts/verify_credentials")
1848 assert %{"id" => id, "source" => %{"privacy" => "private"}} =
1849 json_response_and_validate_schema(conn, 200)
1851 assert id == to_string(user.id)
1855 describe "user relationships" do
1856 setup do: oauth_access(["read:follows"])
1858 test "returns the relationships for the current user", %{user: user, conn: conn} do
1859 %{id: other_user_id} = other_user = insert(:user)
1860 {:ok, _user, _other_user} = User.follow(user, other_user)
1862 assert [%{"id" => ^other_user_id}] =
1864 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1865 |> json_response_and_validate_schema(200)
1867 assert [%{"id" => ^other_user_id}] =
1869 |> get("/api/v1/accounts/relationships?id[]=#{other_user.id}")
1870 |> json_response_and_validate_schema(200)
1873 test "returns an empty list on a bad request", %{conn: conn} do
1874 conn = get(conn, "/api/v1/accounts/relationships", %{})
1876 assert [] = json_response_and_validate_schema(conn, 200)
1880 test "getting a list of mutes" do
1881 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1882 %{id: id1} = other_user1 = insert(:user)
1883 %{id: id2} = other_user2 = insert(:user)
1884 %{id: id3} = other_user3 = insert(:user)
1886 {:ok, _user_relationships} = User.mute(user, other_user1)
1887 {:ok, _user_relationships} = User.mute(user, other_user2)
1888 {:ok, _user_relationships} = User.mute(user, other_user3)
1892 |> get("/api/v1/mutes")
1893 |> json_response_and_validate_schema(200)
1895 assert [id3, id2, id1] == Enum.map(result, & &1["id"])
1899 |> get("/api/v1/mutes?limit=1")
1900 |> json_response_and_validate_schema(200)
1902 assert [%{"id" => ^id3}] = result
1906 |> get("/api/v1/mutes?since_id=#{id1}")
1907 |> json_response_and_validate_schema(200)
1909 assert [%{"id" => ^id3}, %{"id" => ^id2}] = result
1913 |> get("/api/v1/mutes?since_id=#{id1}&max_id=#{id3}")
1914 |> json_response_and_validate_schema(200)
1916 assert [%{"id" => ^id2}] = result
1920 |> get("/api/v1/mutes?since_id=#{id1}&limit=1")
1921 |> json_response_and_validate_schema(200)
1923 assert [%{"id" => ^id3}] = result
1926 test "list of mutes with with_relationships parameter" do
1927 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1928 %{id: id1} = other_user1 = insert(:user)
1929 %{id: id2} = other_user2 = insert(:user)
1930 %{id: id3} = other_user3 = insert(:user)
1932 {:ok, _, _} = User.follow(other_user1, user)
1933 {:ok, _, _} = User.follow(other_user2, user)
1934 {:ok, _, _} = User.follow(other_user3, user)
1936 {:ok, _} = User.mute(user, other_user1)
1937 {:ok, _} = User.mute(user, other_user2)
1938 {:ok, _} = User.mute(user, other_user3)
1943 "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
1947 "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
1951 "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
1955 |> get("/api/v1/mutes?with_relationships=true")
1956 |> json_response_and_validate_schema(200)
1959 test "getting a list of blocks" do
1960 %{user: user, conn: conn} = oauth_access(["read:blocks"])
1961 %{id: id1} = other_user1 = insert(:user)
1962 %{id: id2} = other_user2 = insert(:user)
1963 %{id: id3} = other_user3 = insert(:user)
1965 {:ok, _user_relationship} = User.block(user, other_user1)
1966 {:ok, _user_relationship} = User.block(user, other_user3)
1967 {:ok, _user_relationship} = User.block(user, other_user2)
1971 |> assign(:user, user)
1972 |> get("/api/v1/blocks")
1973 |> json_response_and_validate_schema(200)
1975 assert [id3, id2, id1] == Enum.map(result, & &1["id"])
1979 |> assign(:user, user)
1980 |> get("/api/v1/blocks?limit=1")
1981 |> json_response_and_validate_schema(200)
1983 assert [%{"id" => ^id3}] = result
1987 |> assign(:user, user)
1988 |> get("/api/v1/blocks?since_id=#{id1}")
1989 |> json_response_and_validate_schema(200)
1991 assert [%{"id" => ^id3}, %{"id" => ^id2}] = result
1995 |> assign(:user, user)
1996 |> get("/api/v1/blocks?since_id=#{id1}&max_id=#{id3}")
1997 |> json_response_and_validate_schema(200)
1999 assert [%{"id" => ^id2}] = result
2003 |> assign(:user, user)
2004 |> get("/api/v1/blocks?since_id=#{id1}&limit=1")
2005 |> json_response_and_validate_schema(200)
2007 assert [%{"id" => ^id3}] = result
2011 |> assign(:user, user)
2012 |> get("/api/v1/blocks?limit=2")
2015 ~r{<.+?(?<link>/api[^>]+)>; rel=\"next\"}
2016 |> Regex.named_captures(get_resp_header(conn_res, "link") |> Enum.at(0))
2021 |> json_response_and_validate_schema(200)
2023 assert [%{"id" => ^id3}, %{"id" => ^id2}] = result
2027 |> assign(:user, user)
2029 |> json_response_and_validate_schema(200)
2031 assert [%{"id" => ^id1}] = result
2034 test "account lookup", %{conn: conn} do
2035 %{nickname: acct} = insert(:user, %{nickname: "nickname"})
2036 %{nickname: acct_two} = insert(:user, %{nickname: "nickname@notlocaldoma.in"})
2040 |> get("/api/v1/accounts/lookup?acct=#{acct}")
2041 |> json_response_and_validate_schema(200)
2043 assert %{"acct" => ^acct} = result
2047 |> get("/api/v1/accounts/lookup?acct=#{acct_two}")
2048 |> json_response_and_validate_schema(200)
2050 assert %{"acct" => ^acct_two} = result
2054 |> get("/api/v1/accounts/lookup?acct=unexisting_nickname")
2055 |> json_response_and_validate_schema(404)
2058 test "create a note on a user" do
2059 %{conn: conn} = oauth_access(["write:accounts", "read:follows"])
2060 other_user = insert(:user)
2063 |> put_req_header("content-type", "application/json")
2064 |> post("/api/v1/accounts/#{other_user.id}/note", %{
2065 "comment" => "Example note"
2068 assert [%{"note" => "Example note"}] =
2070 |> put_req_header("content-type", "application/json")
2071 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
2072 |> json_response_and_validate_schema(200)
2075 describe "account endorsements" do
2076 setup do: oauth_access(["read:accounts", "write:accounts", "write:follows"])
2078 setup do: clear_config([:instance, :max_endorsed_users], 1)
2080 test "pin account", %{user: user, conn: conn} do
2081 %{id: id1} = other_user1 = insert(:user)
2083 CommonAPI.follow(user, other_user1)
2085 assert %{"id" => ^id1, "endorsed" => true} =
2087 |> put_req_header("content-type", "application/json")
2088 |> post("/api/v1/accounts/#{id1}/pin")
2089 |> json_response_and_validate_schema(200)
2091 assert [%{"id" => ^id1}] =
2093 |> put_req_header("content-type", "application/json")
2094 |> get("/api/v1/endorsements")
2095 |> json_response_and_validate_schema(200)
2098 test "unpin account", %{user: user, conn: conn} do
2099 %{id: id1} = other_user1 = insert(:user)
2101 CommonAPI.follow(user, other_user1)
2102 User.endorse(user, other_user1)
2104 assert %{"id" => ^id1, "endorsed" => false} =
2106 |> put_req_header("content-type", "application/json")
2107 |> post("/api/v1/accounts/#{id1}/unpin")
2108 |> json_response_and_validate_schema(200)
2112 |> put_req_header("content-type", "application/json")
2113 |> get("/api/v1/endorsements")
2114 |> json_response_and_validate_schema(200)
2117 test "max pinned accounts", %{user: user, conn: conn} do
2118 %{id: id1} = other_user1 = insert(:user)
2119 %{id: id2} = other_user2 = insert(:user)
2121 CommonAPI.follow(user, other_user1)
2122 CommonAPI.follow(user, other_user2)
2125 |> put_req_header("content-type", "application/json")
2126 |> post("/api/v1/accounts/#{id1}/pin")
2127 |> json_response_and_validate_schema(200)
2129 assert %{"error" => "You have already pinned the maximum number of users"} =
2131 |> assign(:user, user)
2132 |> post("/api/v1/accounts/#{id2}/pin")
2133 |> json_response_and_validate_schema(400)
2137 describe "remove from followers" do
2138 setup do: oauth_access(["follow"])
2140 test "removing user from followers", %{conn: conn, user: user} do
2141 %{id: other_user_id} = other_user = insert(:user)
2143 CommonAPI.follow(other_user, user)
2145 assert %{"id" => ^other_user_id, "followed_by" => false} =
2147 |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers")
2148 |> json_response_and_validate_schema(200)
2150 refute User.following?(other_user, user)
2153 test "removing remote user from followers", %{conn: conn, user: user} do
2154 %{id: other_user_id} = other_user = insert(:user, local: false)
2156 CommonAPI.follow(other_user, user)
2158 assert User.following?(other_user, user)
2160 assert %{"id" => ^other_user_id, "followed_by" => false} =
2162 |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers")
2163 |> json_response_and_validate_schema(200)
2165 refute User.following?(other_user, user)
2168 test "removing user from followers errors", %{user: user, conn: conn} do
2170 conn_res = post(conn, "/api/v1/accounts/#{user.id}/remove_from_followers")
2172 assert %{"error" => "Can not unfollow yourself"} =
2173 json_response_and_validate_schema(conn_res, 400)
2175 # remove non existing user
2176 conn_res = post(conn, "/api/v1/accounts/doesntexist/remove_from_followers")
2177 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)