1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
6 use Pleroma.Web.ConnCase
11 alias Pleroma.Web.ActivityPub.ActivityPub
12 alias Pleroma.Web.ActivityPub.InternalFetchActor
13 alias Pleroma.Web.CommonAPI
14 alias Pleroma.Web.OAuth.Token
16 import Pleroma.Factory
18 describe "account fetching" do
19 setup do: clear_config([:instance, :limit_to_local_content])
22 %User{id: user_id} = insert(:user)
24 assert %{"id" => ^user_id} =
26 |> get("/api/v1/accounts/#{user_id}")
27 |> json_response_and_validate_schema(200)
29 assert %{"error" => "Can't find user"} =
31 |> get("/api/v1/accounts/-1")
32 |> json_response_and_validate_schema(404)
35 test "works by nickname" do
38 assert %{"id" => user_id} =
40 |> get("/api/v1/accounts/#{user.nickname}")
41 |> json_response_and_validate_schema(200)
44 test "works by nickname for remote users" do
45 Config.put([:instance, :limit_to_local_content], false)
47 user = insert(:user, nickname: "user@example.com", local: false)
49 assert %{"id" => user_id} =
51 |> get("/api/v1/accounts/#{user.nickname}")
52 |> json_response_and_validate_schema(200)
55 test "respects limit_to_local_content == :all for remote user nicknames" do
56 Config.put([:instance, :limit_to_local_content], :all)
58 user = insert(:user, nickname: "user@example.com", local: false)
61 |> get("/api/v1/accounts/#{user.nickname}")
62 |> json_response_and_validate_schema(404)
65 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
66 Config.put([:instance, :limit_to_local_content], :unauthenticated)
68 user = insert(:user, nickname: "user@example.com", local: false)
69 reading_user = insert(:user)
73 |> get("/api/v1/accounts/#{user.nickname}")
75 assert json_response_and_validate_schema(conn, 404)
79 |> assign(:user, reading_user)
80 |> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"]))
81 |> get("/api/v1/accounts/#{user.nickname}")
83 assert %{"id" => id} = json_response_and_validate_schema(conn, 200)
87 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
88 # Need to set an old-style integer ID to reproduce the problem
89 # (these are no longer assigned to new accounts but were preserved
90 # for existing accounts during the migration to flakeIDs)
91 user_one = insert(:user, %{id: 1212})
92 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
96 |> get("/api/v1/accounts/#{user_one.id}")
97 |> json_response_and_validate_schema(:ok)
101 |> get("/api/v1/accounts/#{user_two.nickname}")
102 |> json_response_and_validate_schema(:ok)
106 |> get("/api/v1/accounts/#{user_two.id}")
107 |> json_response_and_validate_schema(:ok)
109 refute acc_one == acc_two
110 assert acc_two == acc_three
113 test "returns 404 when user is invisible", %{conn: conn} do
114 user = insert(:user, %{invisible: true})
116 assert %{"error" => "Can't find user"} =
118 |> get("/api/v1/accounts/#{user.nickname}")
119 |> json_response_and_validate_schema(404)
122 test "returns 404 for internal.fetch actor", %{conn: conn} do
123 %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor()
125 assert %{"error" => "Can't find user"} =
127 |> get("/api/v1/accounts/internal.fetch")
128 |> json_response_and_validate_schema(404)
132 defp local_and_remote_users do
133 local = insert(:user)
134 remote = insert(:user, local: false)
135 {:ok, local: local, remote: remote}
138 describe "user fetching with restrict unauthenticated profiles for local and remote" do
139 setup do: local_and_remote_users()
141 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
143 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
145 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
146 assert %{"error" => "Can't find user"} ==
148 |> get("/api/v1/accounts/#{local.id}")
149 |> json_response_and_validate_schema(:not_found)
151 assert %{"error" => "Can't find user"} ==
153 |> get("/api/v1/accounts/#{remote.id}")
154 |> json_response_and_validate_schema(:not_found)
157 test "if user is authenticated", %{local: local, remote: remote} do
158 %{conn: conn} = oauth_access(["read"])
160 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
161 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
163 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
164 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
168 describe "user fetching with restrict unauthenticated profiles for local" do
169 setup do: local_and_remote_users()
171 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
173 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
174 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
176 assert json_response_and_validate_schema(res_conn, :not_found) == %{
177 "error" => "Can't find user"
180 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
181 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
184 test "if user is authenticated", %{local: local, remote: remote} do
185 %{conn: conn} = oauth_access(["read"])
187 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
188 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
190 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
191 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
195 describe "user fetching with restrict unauthenticated profiles for remote" do
196 setup do: local_and_remote_users()
198 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
200 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
201 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
202 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
204 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
206 assert json_response_and_validate_schema(res_conn, :not_found) == %{
207 "error" => "Can't find user"
211 test "if user is authenticated", %{local: local, remote: remote} do
212 %{conn: conn} = oauth_access(["read"])
214 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
215 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
217 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
218 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
222 describe "user timelines" do
223 setup do: oauth_access(["read:statuses"])
225 test "works with announces that are just addressed to public", %{conn: conn} do
226 user = insert(:user, ap_id: "https://honktest/u/test", local: false)
227 other_user = insert(:user)
229 {:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"})
233 "@context" => "https://www.w3.org/ns/activitystreams",
234 "actor" => "https://honktest/u/test",
235 "id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx",
236 "object" => post.data["object"],
237 "published" => "2019-06-25T19:33:58Z",
238 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
241 |> ActivityPub.persist(local: false)
245 |> get("/api/v1/accounts/#{user.id}/statuses")
246 |> json_response_and_validate_schema(200)
248 assert [%{"id" => id}] = resp
249 assert id == announce.id
252 test "respects blocks", %{user: user_one, conn: conn} do
253 user_two = insert(:user)
254 user_three = insert(:user)
256 User.block(user_one, user_two)
258 {:ok, activity} = CommonAPI.post(user_two, %{status: "User one sux0rz"})
259 {:ok, repeat} = CommonAPI.repeat(activity.id, user_three)
263 |> get("/api/v1/accounts/#{user_two.id}/statuses")
264 |> json_response_and_validate_schema(200)
266 assert [%{"id" => id}] = resp
267 assert id == activity.id
269 # Even a blocked user will deliver the full user timeline, there would be
270 # no point in looking at a blocked users timeline otherwise
273 |> get("/api/v1/accounts/#{user_two.id}/statuses")
274 |> json_response_and_validate_schema(200)
276 assert [%{"id" => id}] = resp
277 assert id == activity.id
279 # Third user's timeline includes the repeat when viewed by unauthenticated user
282 |> get("/api/v1/accounts/#{user_three.id}/statuses")
283 |> json_response_and_validate_schema(200)
285 assert [%{"id" => id}] = resp
286 assert id == repeat.id
288 # When viewing a third user's timeline, the blocked users' statuses will NOT be shown
289 resp = get(conn, "/api/v1/accounts/#{user_three.id}/statuses")
291 assert [] == json_response_and_validate_schema(resp, 200)
294 test "gets users statuses", %{conn: conn} do
295 user_one = insert(:user)
296 user_two = insert(:user)
297 user_three = insert(:user)
299 {:ok, _user_three} = User.follow(user_three, user_one)
301 {:ok, activity} = CommonAPI.post(user_one, %{status: "HI!!!"})
303 {:ok, direct_activity} =
304 CommonAPI.post(user_one, %{
305 status: "Hi, @#{user_two.nickname}.",
309 {:ok, private_activity} =
310 CommonAPI.post(user_one, %{status: "private", visibility: "private"})
315 |> get("/api/v1/accounts/#{user_one.id}/statuses")
316 |> json_response_and_validate_schema(200)
318 assert [%{"id" => id}] = resp
319 assert id == to_string(activity.id)
323 |> assign(:user, user_two)
324 |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"]))
325 |> get("/api/v1/accounts/#{user_one.id}/statuses")
326 |> json_response_and_validate_schema(200)
328 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
329 assert id_one == to_string(direct_activity.id)
330 assert id_two == to_string(activity.id)
334 |> assign(:user, user_three)
335 |> assign(:token, insert(:oauth_token, user: user_three, scopes: ["read:statuses"]))
336 |> get("/api/v1/accounts/#{user_one.id}/statuses")
337 |> json_response_and_validate_schema(200)
339 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
340 assert id_one == to_string(private_activity.id)
341 assert id_two == to_string(activity.id)
344 test "unimplemented pinned statuses feature", %{conn: conn} do
345 note = insert(:note_activity)
346 user = User.get_cached_by_ap_id(note.data["actor"])
348 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?pinned=true")
350 assert json_response_and_validate_schema(conn, 200) == []
353 test "gets an users media, excludes reblogs", %{conn: conn} do
354 note = insert(:note_activity)
355 user = User.get_cached_by_ap_id(note.data["actor"])
356 other_user = insert(:user)
359 content_type: "image/jpg",
360 path: Path.absname("test/fixtures/image.jpg"),
361 filename: "an_image.jpg"
364 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
366 {:ok, %{id: image_post_id}} = CommonAPI.post(user, %{status: "cofe", media_ids: [media_id]})
368 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: other_user.ap_id)
370 {:ok, %{id: other_image_post_id}} =
371 CommonAPI.post(other_user, %{status: "cofe2", media_ids: [media_id]})
373 {:ok, _announce} = CommonAPI.repeat(other_image_post_id, user)
375 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true")
377 assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
379 conn = get(build_conn(), "/api/v1/accounts/#{user.id}/statuses?only_media=1")
381 assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
384 test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
385 {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"})
386 {:ok, _} = CommonAPI.repeat(post_id, user)
388 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
389 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
391 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=1")
392 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
395 test "filters user's statuses by a hashtag", %{user: user, conn: conn} do
396 {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "#hashtag"})
397 {:ok, _post} = CommonAPI.post(user, %{status: "hashtag"})
399 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?tagged=hashtag")
400 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
403 test "the user views their own timelines and excludes direct messages", %{
407 {:ok, %{id: public_activity_id}} =
408 CommonAPI.post(user, %{status: ".", visibility: "public"})
410 {:ok, _direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
412 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_visibilities[]=direct")
413 assert [%{"id" => ^public_activity_id}] = json_response_and_validate_schema(conn, 200)
417 defp local_and_remote_activities(%{local: local, remote: remote}) do
418 insert(:note_activity, user: local)
419 insert(:note_activity, user: remote, local: false)
424 describe "statuses with restrict unauthenticated profiles for local and remote" do
425 setup do: local_and_remote_users()
426 setup :local_and_remote_activities
428 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
430 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
432 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
433 assert %{"error" => "Can't find user"} ==
435 |> get("/api/v1/accounts/#{local.id}/statuses")
436 |> json_response_and_validate_schema(:not_found)
438 assert %{"error" => "Can't find user"} ==
440 |> get("/api/v1/accounts/#{remote.id}/statuses")
441 |> json_response_and_validate_schema(:not_found)
444 test "if user is authenticated", %{local: local, remote: remote} do
445 %{conn: conn} = oauth_access(["read"])
447 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
448 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
450 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
451 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
455 describe "statuses with restrict unauthenticated profiles for local" do
456 setup do: local_and_remote_users()
457 setup :local_and_remote_activities
459 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
461 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
462 assert %{"error" => "Can't find user"} ==
464 |> get("/api/v1/accounts/#{local.id}/statuses")
465 |> json_response_and_validate_schema(:not_found)
467 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
468 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
471 test "if user is authenticated", %{local: local, remote: remote} do
472 %{conn: conn} = oauth_access(["read"])
474 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
475 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
477 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
478 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
482 describe "statuses with restrict unauthenticated profiles for remote" do
483 setup do: local_and_remote_users()
484 setup :local_and_remote_activities
486 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
488 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
489 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
490 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
492 assert %{"error" => "Can't find user"} ==
494 |> get("/api/v1/accounts/#{remote.id}/statuses")
495 |> json_response_and_validate_schema(:not_found)
498 test "if user is authenticated", %{local: local, remote: remote} do
499 %{conn: conn} = oauth_access(["read"])
501 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
502 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
504 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
505 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
509 describe "followers" do
510 setup do: oauth_access(["read:accounts"])
512 test "getting followers", %{user: user, conn: conn} do
513 other_user = insert(:user)
514 {:ok, %{id: user_id}} = User.follow(user, other_user)
516 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
518 assert [%{"id" => ^user_id}] = json_response_and_validate_schema(conn, 200)
521 test "getting followers, hide_followers", %{user: user, conn: conn} do
522 other_user = insert(:user, hide_followers: true)
523 {:ok, _user} = User.follow(user, other_user)
525 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
527 assert [] == json_response_and_validate_schema(conn, 200)
530 test "getting followers, hide_followers, same user requesting" do
532 other_user = insert(:user, hide_followers: true)
533 {:ok, _user} = User.follow(user, other_user)
537 |> assign(:user, other_user)
538 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
539 |> get("/api/v1/accounts/#{other_user.id}/followers")
541 refute [] == json_response_and_validate_schema(conn, 200)
544 test "getting followers, pagination", %{user: user, conn: conn} do
545 {:ok, %User{id: follower1_id}} = :user |> insert() |> User.follow(user)
546 {:ok, %User{id: follower2_id}} = :user |> insert() |> User.follow(user)
547 {:ok, %User{id: follower3_id}} = :user |> insert() |> User.follow(user)
549 assert [%{"id" => ^follower3_id}, %{"id" => ^follower2_id}] =
551 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1_id}")
552 |> json_response_and_validate_schema(200)
554 assert [%{"id" => ^follower2_id}, %{"id" => ^follower1_id}] =
556 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3_id}")
557 |> json_response_and_validate_schema(200)
559 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3_id}")
561 assert [%{"id" => ^follower2_id}] = json_response_and_validate_schema(res_conn, 200)
563 assert [link_header] = get_resp_header(res_conn, "link")
564 assert link_header =~ ~r/min_id=#{follower2_id}/
565 assert link_header =~ ~r/max_id=#{follower2_id}/
569 describe "following" do
570 setup do: oauth_access(["read:accounts"])
572 test "getting following", %{user: user, conn: conn} do
573 other_user = insert(:user)
574 {:ok, user} = User.follow(user, other_user)
576 conn = get(conn, "/api/v1/accounts/#{user.id}/following")
578 assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200)
579 assert id == to_string(other_user.id)
582 test "getting following, hide_follows, other user requesting" do
583 user = insert(:user, hide_follows: true)
584 other_user = insert(:user)
585 {:ok, user} = User.follow(user, other_user)
589 |> assign(:user, other_user)
590 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
591 |> get("/api/v1/accounts/#{user.id}/following")
593 assert [] == json_response_and_validate_schema(conn, 200)
596 test "getting following, hide_follows, same user requesting" do
597 user = insert(:user, hide_follows: true)
598 other_user = insert(:user)
599 {:ok, user} = User.follow(user, other_user)
603 |> assign(:user, user)
604 |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"]))
605 |> get("/api/v1/accounts/#{user.id}/following")
607 refute [] == json_response_and_validate_schema(conn, 200)
610 test "getting following, pagination", %{user: user, conn: conn} do
611 following1 = insert(:user)
612 following2 = insert(:user)
613 following3 = insert(:user)
614 {:ok, _} = User.follow(user, following1)
615 {:ok, _} = User.follow(user, following2)
616 {:ok, _} = User.follow(user, following3)
618 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
620 assert [%{"id" => id3}, %{"id" => id2}] = json_response_and_validate_schema(res_conn, 200)
621 assert id3 == following3.id
622 assert id2 == following2.id
624 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
626 assert [%{"id" => id2}, %{"id" => id1}] = json_response_and_validate_schema(res_conn, 200)
627 assert id2 == following2.id
628 assert id1 == following1.id
631 get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
633 assert [%{"id" => id2}] = json_response_and_validate_schema(res_conn, 200)
634 assert id2 == following2.id
636 assert [link_header] = get_resp_header(res_conn, "link")
637 assert link_header =~ ~r/min_id=#{following2.id}/
638 assert link_header =~ ~r/max_id=#{following2.id}/
642 describe "follow/unfollow" do
643 setup do: oauth_access(["follow"])
645 test "following / unfollowing a user", %{conn: conn} do
646 %{id: other_user_id, nickname: other_user_nickname} = insert(:user)
648 assert %{"id" => _id, "following" => true} =
650 |> post("/api/v1/accounts/#{other_user_id}/follow")
651 |> json_response_and_validate_schema(200)
653 assert %{"id" => _id, "following" => false} =
655 |> post("/api/v1/accounts/#{other_user_id}/unfollow")
656 |> json_response_and_validate_schema(200)
658 assert %{"id" => ^other_user_id} =
660 |> put_req_header("content-type", "application/json")
661 |> post("/api/v1/follows", %{"uri" => other_user_nickname})
662 |> json_response_and_validate_schema(200)
665 test "cancelling follow request", %{conn: conn} do
666 %{id: other_user_id} = insert(:user, %{locked: true})
668 assert %{"id" => ^other_user_id, "following" => false, "requested" => true} =
670 |> post("/api/v1/accounts/#{other_user_id}/follow")
671 |> json_response_and_validate_schema(:ok)
673 assert %{"id" => ^other_user_id, "following" => false, "requested" => false} =
675 |> post("/api/v1/accounts/#{other_user_id}/unfollow")
676 |> json_response_and_validate_schema(:ok)
679 test "following without reblogs" do
680 %{conn: conn} = oauth_access(["follow", "read:statuses"])
681 followed = insert(:user)
682 other_user = insert(:user)
684 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=false")
686 assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200)
688 {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
689 {:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)
693 |> get("/api/v1/timelines/home")
694 |> json_response(200)
696 assert %{"showing_reblogs" => true} =
698 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
699 |> json_response_and_validate_schema(200)
701 assert [%{"id" => ^reblog_id}] =
703 |> get("/api/v1/timelines/home")
704 |> json_response(200)
707 test "following / unfollowing errors", %{user: user, conn: conn} do
709 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
711 assert %{"error" => "Can not follow yourself"} =
712 json_response_and_validate_schema(conn_res, 400)
715 user = User.get_cached_by_id(user.id)
716 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
718 assert %{"error" => "Can not unfollow yourself"} =
719 json_response_and_validate_schema(conn_res, 400)
721 # self follow via uri
722 user = User.get_cached_by_id(user.id)
724 assert %{"error" => "Can not follow yourself"} =
726 |> put_req_header("content-type", "multipart/form-data")
727 |> post("/api/v1/follows", %{"uri" => user.nickname})
728 |> json_response_and_validate_schema(400)
730 # follow non existing user
731 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
732 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
734 # follow non existing user via uri
737 |> put_req_header("content-type", "multipart/form-data")
738 |> post("/api/v1/follows", %{"uri" => "doesntexist"})
740 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
742 # unfollow non existing user
743 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
744 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
748 describe "mute/unmute" do
749 setup do: oauth_access(["write:mutes"])
751 test "with notifications", %{conn: conn} do
752 other_user = insert(:user)
754 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} =
756 |> put_req_header("content-type", "application/json")
757 |> post("/api/v1/accounts/#{other_user.id}/mute")
758 |> json_response_and_validate_schema(200)
760 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
762 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
763 json_response_and_validate_schema(conn, 200)
766 test "without notifications", %{conn: conn} do
767 other_user = insert(:user)
771 |> put_req_header("content-type", "multipart/form-data")
772 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
774 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} =
775 json_response_and_validate_schema(ret_conn, 200)
777 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
779 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
780 json_response_and_validate_schema(conn, 200)
784 describe "pinned statuses" do
787 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
788 %{conn: conn} = oauth_access(["read:statuses"], user: user)
790 [conn: conn, user: user, activity: activity]
793 test "returns pinned statuses", %{conn: conn, user: user, activity: %{id: activity_id}} do
794 {:ok, _} = CommonAPI.pin(activity_id, user)
796 assert [%{"id" => ^activity_id, "pinned" => true}] =
798 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
799 |> json_response_and_validate_schema(200)
803 test "blocking / unblocking a user" do
804 %{conn: conn} = oauth_access(["follow"])
805 other_user = insert(:user)
807 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block")
809 assert %{"id" => _id, "blocking" => true} = json_response_and_validate_schema(ret_conn, 200)
811 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock")
813 assert %{"id" => _id, "blocking" => false} = json_response_and_validate_schema(conn, 200)
816 describe "create account by app" do
820 email: "lain@example.org",
821 password: "PlzDontHackLain",
825 [valid_params: valid_params]
828 setup do: clear_config([:instance, :account_activation_required])
830 test "Account registration via Application", %{conn: conn} do
833 |> put_req_header("content-type", "application/json")
834 |> post("/api/v1/apps", %{
835 client_name: "client_name",
836 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
837 scopes: "read, write, follow"
841 "client_id" => client_id,
842 "client_secret" => client_secret,
844 "name" => "client_name",
845 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
848 } = json_response_and_validate_schema(conn, 200)
851 post(conn, "/oauth/token", %{
852 grant_type: "client_credentials",
853 client_id: client_id,
854 client_secret: client_secret
857 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
858 json_response(conn, 200)
861 token_from_db = Repo.get_by(Token, token: token)
864 assert scope == "read write follow"
868 |> put_req_header("content-type", "multipart/form-data")
869 |> put_req_header("authorization", "Bearer " <> token)
870 |> post("/api/v1/accounts", %{
872 email: "lain@example.org",
873 password: "PlzDontHackLain",
879 "access_token" => token,
880 "created_at" => _created_at,
882 "token_type" => "Bearer"
883 } = json_response_and_validate_schema(conn, 200)
885 token_from_db = Repo.get_by(Token, token: token)
887 token_from_db = Repo.preload(token_from_db, :user)
888 assert token_from_db.user
890 assert token_from_db.user.confirmation_pending
893 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
894 _user = insert(:user, email: "lain@example.org")
895 app_token = insert(:oauth_token, user: nil)
899 |> put_req_header("authorization", "Bearer " <> app_token.token)
900 |> put_req_header("content-type", "application/json")
901 |> post("/api/v1/accounts", valid_params)
903 assert json_response_and_validate_schema(res, 400) == %{
904 "error" => "{\"email\":[\"has already been taken\"]}"
908 test "returns bad_request if missing required params", %{
910 valid_params: valid_params
912 app_token = insert(:oauth_token, user: nil)
916 |> put_req_header("authorization", "Bearer " <> app_token.token)
917 |> put_req_header("content-type", "application/json")
919 res = post(conn, "/api/v1/accounts", valid_params)
920 assert json_response_and_validate_schema(res, 200)
922 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
923 |> Stream.zip(Map.delete(valid_params, :email))
924 |> Enum.each(fn {ip, {attr, _}} ->
927 |> Map.put(:remote_ip, ip)
928 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
929 |> json_response_and_validate_schema(400)
932 "error" => "Missing field: #{attr}.",
935 "message" => "Missing field: #{attr}",
936 "source" => %{"pointer" => "/#{attr}"},
937 "title" => "Invalid value"
944 setup do: clear_config([:instance, :account_activation_required])
946 test "returns bad_request if missing email params when :account_activation_required is enabled",
947 %{conn: conn, valid_params: valid_params} do
948 Pleroma.Config.put([:instance, :account_activation_required], true)
950 app_token = insert(:oauth_token, user: nil)
954 |> put_req_header("authorization", "Bearer " <> app_token.token)
955 |> put_req_header("content-type", "application/json")
959 |> Map.put(:remote_ip, {127, 0, 0, 5})
960 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
962 assert json_response_and_validate_schema(res, 400) ==
963 %{"error" => "Missing parameter: email"}
967 |> Map.put(:remote_ip, {127, 0, 0, 6})
968 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
970 assert json_response_and_validate_schema(res, 400) == %{
971 "error" => "{\"email\":[\"can't be blank\"]}"
975 test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
976 app_token = insert(:oauth_token, user: nil)
977 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
981 |> put_req_header("content-type", "application/json")
982 |> Map.put(:remote_ip, {127, 0, 0, 7})
983 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
985 assert json_response_and_validate_schema(res, 200)
988 test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
989 app_token = insert(:oauth_token, user: nil)
990 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
994 |> put_req_header("content-type", "application/json")
995 |> Map.put(:remote_ip, {127, 0, 0, 8})
996 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
998 assert json_response_and_validate_schema(res, 200)
1001 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
1004 |> put_req_header("authorization", "Bearer " <> "invalid-token")
1005 |> put_req_header("content-type", "multipart/form-data")
1006 |> post("/api/v1/accounts", valid_params)
1008 assert json_response_and_validate_schema(res, 403) == %{"error" => "Invalid credentials"}
1011 test "registration from trusted app" do
1012 clear_config([Pleroma.Captcha, :enabled], true)
1013 app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
1017 |> post("/oauth/token", %{
1018 "grant_type" => "client_credentials",
1019 "client_id" => app.client_id,
1020 "client_secret" => app.client_secret
1023 assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
1027 |> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
1028 |> put_req_header("content-type", "multipart/form-data")
1029 |> post("/api/v1/accounts", %{
1030 nickname: "nickanme",
1032 email: "email@example.com",
1035 password: "some_password",
1036 confirm: "some_password"
1038 |> json_response_and_validate_schema(200)
1041 "access_token" => access_token,
1043 "scope" => ["read", "write", "follow", "push"],
1044 "token_type" => "Bearer"
1049 |> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
1050 |> get("/api/v1/accounts/verify_credentials")
1051 |> json_response_and_validate_schema(200)
1056 "display_name" => "Lain",
1057 "follow_requests_count" => 0,
1058 "followers_count" => 0,
1059 "following_count" => 0,
1066 "actor_type" => "Person",
1067 "discoverable" => false,
1068 "no_rich_text" => false,
1071 "privacy" => "public",
1072 "sensitive" => false
1074 "statuses_count" => 0,
1075 "username" => "Lain"
1080 describe "create account by app / rate limit" do
1081 setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
1083 test "respects rate limit setting", %{conn: conn} do
1084 app_token = insert(:oauth_token, user: nil)
1088 |> put_req_header("authorization", "Bearer " <> app_token.token)
1089 |> Map.put(:remote_ip, {15, 15, 15, 15})
1090 |> put_req_header("content-type", "multipart/form-data")
1095 |> post("/api/v1/accounts", %{
1096 username: "#{i}lain",
1097 email: "#{i}lain@example.org",
1098 password: "PlzDontHackLain",
1103 "access_token" => token,
1104 "created_at" => _created_at,
1106 "token_type" => "Bearer"
1107 } = json_response_and_validate_schema(conn, 200)
1109 token_from_db = Repo.get_by(Token, token: token)
1110 assert token_from_db
1111 token_from_db = Repo.preload(token_from_db, :user)
1112 assert token_from_db.user
1114 assert token_from_db.user.confirmation_pending
1118 post(conn, "/api/v1/accounts", %{
1120 email: "6lain@example.org",
1121 password: "PlzDontHackLain",
1125 assert json_response_and_validate_schema(conn, :too_many_requests) == %{
1126 "error" => "Throttled"
1131 describe "create account with enabled captcha" do
1132 setup %{conn: conn} do
1133 app_token = insert(:oauth_token, user: nil)
1137 |> put_req_header("authorization", "Bearer " <> app_token.token)
1138 |> put_req_header("content-type", "multipart/form-data")
1143 setup do: clear_config([Pleroma.Captcha, :enabled], true)
1145 test "creates an account and returns 200 if captcha is valid", %{conn: conn} do
1146 %{token: token, answer_data: answer_data} = Pleroma.Captcha.new()
1150 email: "lain@example.org",
1151 password: "PlzDontHackLain",
1153 captcha_solution: Pleroma.Captcha.Mock.solution(),
1154 captcha_token: token,
1155 captcha_answer_data: answer_data
1159 "access_token" => access_token,
1161 "scope" => ["read"],
1162 "token_type" => "Bearer"
1165 |> post("/api/v1/accounts", params)
1166 |> json_response_and_validate_schema(:ok)
1168 assert Token |> Repo.get_by(token: access_token) |> Repo.preload(:user) |> Map.get(:user)
1170 Cachex.del(:used_captcha_cache, token)
1173 test "returns 400 if any captcha field is not provided", %{conn: conn} do
1174 captcha_fields = [:captcha_solution, :captcha_token, :captcha_answer_data]
1178 email: "lain@example.org",
1179 password: "PlzDontHackLain",
1181 captcha_solution: "xx",
1182 captcha_token: "xx",
1183 captcha_answer_data: "xx"
1186 for field <- captcha_fields do
1188 "error" => "{\"captcha\":[\"Invalid CAPTCHA (Missing parameter: #{field})\"]}"
1193 |> post("/api/v1/accounts", Map.delete(valid_params, field))
1194 |> json_response_and_validate_schema(:bad_request)
1198 test "returns an error if captcha is invalid", %{conn: conn} do
1201 email: "lain@example.org",
1202 password: "PlzDontHackLain",
1204 captcha_solution: "cofe",
1205 captcha_token: "cofe",
1206 captcha_answer_data: "cofe"
1209 assert %{"error" => "{\"captcha\":[\"Invalid answer data\"]}"} ==
1211 |> post("/api/v1/accounts", params)
1212 |> json_response_and_validate_schema(:bad_request)
1216 describe "GET /api/v1/accounts/:id/lists - account_lists" do
1217 test "returns lists to which the account belongs" do
1218 %{user: user, conn: conn} = oauth_access(["read:lists"])
1219 other_user = insert(:user)
1220 assert {:ok, %Pleroma.List{id: list_id} = list} = Pleroma.List.create("Test List", user)
1221 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
1223 assert [%{"id" => list_id, "title" => "Test List"}] =
1225 |> get("/api/v1/accounts/#{other_user.id}/lists")
1226 |> json_response_and_validate_schema(200)
1230 describe "verify_credentials" do
1231 test "verify_credentials" do
1232 %{user: user, conn: conn} = oauth_access(["read:accounts"])
1233 [notification | _] = insert_list(7, :notification, user: user)
1234 Pleroma.Notification.set_read_up_to(user, notification.id)
1235 conn = get(conn, "/api/v1/accounts/verify_credentials")
1237 response = json_response_and_validate_schema(conn, 200)
1239 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
1240 assert response["pleroma"]["chat_token"]
1241 assert response["pleroma"]["unread_notifications_count"] == 6
1242 assert id == to_string(user.id)
1245 test "verify_credentials default scope unlisted" do
1246 user = insert(:user, default_scope: "unlisted")
1247 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1249 conn = get(conn, "/api/v1/accounts/verify_credentials")
1251 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} =
1252 json_response_and_validate_schema(conn, 200)
1254 assert id == to_string(user.id)
1257 test "locked accounts" do
1258 user = insert(:user, default_scope: "private")
1259 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1261 conn = get(conn, "/api/v1/accounts/verify_credentials")
1263 assert %{"id" => id, "source" => %{"privacy" => "private"}} =
1264 json_response_and_validate_schema(conn, 200)
1266 assert id == to_string(user.id)
1270 describe "user relationships" do
1271 setup do: oauth_access(["read:follows"])
1273 test "returns the relationships for the current user", %{user: user, conn: conn} do
1274 %{id: other_user_id} = other_user = insert(:user)
1275 {:ok, _user} = User.follow(user, other_user)
1277 assert [%{"id" => ^other_user_id}] =
1279 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1280 |> json_response_and_validate_schema(200)
1282 assert [%{"id" => ^other_user_id}] =
1284 |> get("/api/v1/accounts/relationships?id[]=#{other_user.id}")
1285 |> json_response_and_validate_schema(200)
1288 test "returns an empty list on a bad request", %{conn: conn} do
1289 conn = get(conn, "/api/v1/accounts/relationships", %{})
1291 assert [] = json_response_and_validate_schema(conn, 200)
1295 test "getting a list of mutes" do
1296 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1297 other_user = insert(:user)
1299 {:ok, _user_relationships} = User.mute(user, other_user)
1301 conn = get(conn, "/api/v1/mutes")
1303 other_user_id = to_string(other_user.id)
1304 assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200)
1307 test "getting a list of blocks" do
1308 %{user: user, conn: conn} = oauth_access(["read:blocks"])
1309 other_user = insert(:user)
1311 {:ok, _user_relationship} = User.block(user, other_user)
1315 |> assign(:user, user)
1316 |> get("/api/v1/blocks")
1318 other_user_id = to_string(other_user.id)
1319 assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200)