# Pleroma: A lightweight social networking server # Copyright © 2017-2019 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do use Pleroma.Web.ConnCase alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI import Pleroma.Factory describe "account fetching" do test "works by id" do user = insert(:user) conn = build_conn() |> get("/api/v1/accounts/#{user.id}") assert %{"id" => id} = json_response(conn, 200) assert id == to_string(user.id) conn = build_conn() |> get("/api/v1/accounts/-1") assert %{"error" => "Can't find user"} = json_response(conn, 404) end test "works by nickname" do user = insert(:user) conn = build_conn() |> get("/api/v1/accounts/#{user.nickname}") assert %{"id" => id} = json_response(conn, 200) assert id == user.id end test "works by nickname for remote users" do limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) Pleroma.Config.put([:instance, :limit_to_local_content], false) user = insert(:user, nickname: "user@example.com", local: false) conn = build_conn() |> get("/api/v1/accounts/#{user.nickname}") Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) assert %{"id" => id} = json_response(conn, 200) assert id == user.id end test "respects limit_to_local_content == :all for remote user nicknames" do limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) Pleroma.Config.put([:instance, :limit_to_local_content], :all) user = insert(:user, nickname: "user@example.com", local: false) conn = build_conn() |> get("/api/v1/accounts/#{user.nickname}") Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) assert json_response(conn, 404) end test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) user = insert(:user, nickname: "user@example.com", local: false) reading_user = insert(:user) conn = build_conn() |> get("/api/v1/accounts/#{user.nickname}") assert json_response(conn, 404) conn = build_conn() |> assign(:user, reading_user) |> get("/api/v1/accounts/#{user.nickname}") Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) assert %{"id" => id} = json_response(conn, 200) assert id == user.id end test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do # Need to set an old-style integer ID to reproduce the problem # (these are no longer assigned to new accounts but were preserved # for existing accounts during the migration to flakeIDs) user_one = insert(:user, %{id: 1212}) user_two = insert(:user, %{nickname: "#{user_one.id}garbage"}) resp_one = conn |> get("/api/v1/accounts/#{user_one.id}") resp_two = conn |> get("/api/v1/accounts/#{user_two.nickname}") resp_three = conn |> get("/api/v1/accounts/#{user_two.id}") acc_one = json_response(resp_one, 200) acc_two = json_response(resp_two, 200) acc_three = json_response(resp_three, 200) refute acc_one == acc_two assert acc_two == acc_three end end describe "user timelines" do test "gets a users statuses", %{conn: conn} do user_one = insert(:user) user_two = insert(:user) user_three = insert(:user) {:ok, user_three} = User.follow(user_three, user_one) {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"}) {:ok, direct_activity} = CommonAPI.post(user_one, %{ "status" => "Hi, @#{user_two.nickname}.", "visibility" => "direct" }) {:ok, private_activity} = CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"}) resp = conn |> get("/api/v1/accounts/#{user_one.id}/statuses") assert [%{"id" => id}] = json_response(resp, 200) assert id == to_string(activity.id) resp = conn |> assign(:user, user_two) |> get("/api/v1/accounts/#{user_one.id}/statuses") assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) assert id_one == to_string(direct_activity.id) assert id_two == to_string(activity.id) resp = conn |> assign(:user, user_three) |> get("/api/v1/accounts/#{user_one.id}/statuses") assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) assert id_one == to_string(private_activity.id) assert id_two == to_string(activity.id) end test "unimplemented pinned statuses feature", %{conn: conn} do note = insert(:note_activity) user = User.get_cached_by_ap_id(note.data["actor"]) conn = conn |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") assert json_response(conn, 200) == [] end test "gets an users media", %{conn: conn} do note = insert(:note_activity) user = User.get_cached_by_ap_id(note.data["actor"]) file = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id) {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]}) conn = conn |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(image_post.id) conn = build_conn() |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(image_post.id) end test "gets a user's statuses without reblogs", %{conn: conn} do user = insert(:user) {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"}) {:ok, _, _} = CommonAPI.repeat(post.id, user) conn = conn |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(post.id) conn = conn |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(post.id) end test "filters user's statuses by a hashtag", %{conn: conn} do user = insert(:user) {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"}) {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"}) conn = conn |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(post.id) end end describe "followers" do test "getting followers", %{conn: conn} do user = insert(:user) other_user = insert(:user) {:ok, user} = User.follow(user, other_user) conn = conn |> get("/api/v1/accounts/#{other_user.id}/followers") assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(user.id) end test "getting followers, hide_followers", %{conn: conn} do user = insert(:user) other_user = insert(:user, %{info: %{hide_followers: true}}) {:ok, _user} = User.follow(user, other_user) conn = conn |> get("/api/v1/accounts/#{other_user.id}/followers") assert [] == json_response(conn, 200) end test "getting followers, hide_followers, same user requesting", %{conn: conn} do user = insert(:user) other_user = insert(:user, %{info: %{hide_followers: true}}) {:ok, _user} = User.follow(user, other_user) conn = conn |> assign(:user, other_user) |> get("/api/v1/accounts/#{other_user.id}/followers") refute [] == json_response(conn, 200) end test "getting followers, pagination", %{conn: conn} do user = insert(:user) follower1 = insert(:user) follower2 = insert(:user) follower3 = insert(:user) {:ok, _} = User.follow(follower1, user) {:ok, _} = User.follow(follower2, user) {:ok, _} = User.follow(follower3, user) conn = conn |> assign(:user, user) res_conn = conn |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}") assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200) assert id3 == follower3.id assert id2 == follower2.id res_conn = conn |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}") assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200) assert id2 == follower2.id assert id1 == follower1.id res_conn = conn |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}") assert [%{"id" => id2}] = json_response(res_conn, 200) assert id2 == follower2.id assert [link_header] = get_resp_header(res_conn, "link") assert link_header =~ ~r/min_id=#{follower2.id}/ assert link_header =~ ~r/max_id=#{follower2.id}/ end end describe "following" do test "getting following", %{conn: conn} do user = insert(:user) other_user = insert(:user) {:ok, user} = User.follow(user, other_user) conn = conn |> get("/api/v1/accounts/#{user.id}/following") assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(other_user.id) end test "getting following, hide_follows", %{conn: conn} do user = insert(:user, %{info: %{hide_follows: true}}) other_user = insert(:user) {:ok, user} = User.follow(user, other_user) conn = conn |> get("/api/v1/accounts/#{user.id}/following") assert [] == json_response(conn, 200) end test "getting following, hide_follows, same user requesting", %{conn: conn} do user = insert(:user, %{info: %{hide_follows: true}}) other_user = insert(:user) {:ok, user} = User.follow(user, other_user) conn = conn |> assign(:user, user) |> get("/api/v1/accounts/#{user.id}/following") refute [] == json_response(conn, 200) end test "getting following, pagination", %{conn: conn} do user = insert(:user) following1 = insert(:user) following2 = insert(:user) following3 = insert(:user) {:ok, _} = User.follow(user, following1) {:ok, _} = User.follow(user, following2) {:ok, _} = User.follow(user, following3) conn = conn |> assign(:user, user) res_conn = conn |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}") assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200) assert id3 == following3.id assert id2 == following2.id res_conn = conn |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}") assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200) assert id2 == following2.id assert id1 == following1.id res_conn = conn |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}") assert [%{"id" => id2}] = json_response(res_conn, 200) assert id2 == following2.id assert [link_header] = get_resp_header(res_conn, "link") assert link_header =~ ~r/min_id=#{following2.id}/ assert link_header =~ ~r/max_id=#{following2.id}/ end end describe "follow/unfollow" do test "following / unfollowing a user", %{conn: conn} do user = insert(:user) other_user = insert(:user) conn = conn |> assign(:user, user) |> post("/api/v1/accounts/#{other_user.id}/follow") assert %{"id" => _id, "following" => true} = json_response(conn, 200) user = User.get_cached_by_id(user.id) conn = build_conn() |> assign(:user, user) |> post("/api/v1/accounts/#{other_user.id}/unfollow") assert %{"id" => _id, "following" => false} = json_response(conn, 200) user = User.get_cached_by_id(user.id) conn = build_conn() |> assign(:user, user) |> post("/api/v1/follows", %{"uri" => other_user.nickname}) assert %{"id" => id} = json_response(conn, 200) assert id == to_string(other_user.id) end test "following without reblogs" do follower = insert(:user) followed = insert(:user) other_user = insert(:user) conn = build_conn() |> assign(:user, follower) |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false") assert %{"showing_reblogs" => false} = json_response(conn, 200) {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"}) {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed) conn = build_conn() |> assign(:user, User.get_cached_by_id(follower.id)) |> get("/api/v1/timelines/home") assert [] == json_response(conn, 200) conn = build_conn() |> assign(:user, follower) |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true") assert %{"showing_reblogs" => true} = json_response(conn, 200) conn = build_conn() |> assign(:user, User.get_cached_by_id(follower.id)) |> get("/api/v1/timelines/home") expected_activity_id = reblog.id assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200) end test "following / unfollowing errors" do user = insert(:user) conn = build_conn() |> assign(:user, user) # self follow conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow") assert %{"error" => "Record not found"} = json_response(conn_res, 404) # self unfollow user = User.get_cached_by_id(user.id) conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow") assert %{"error" => "Record not found"} = json_response(conn_res, 404) # self follow via uri user = User.get_cached_by_id(user.id) conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname}) assert %{"error" => "Record not found"} = json_response(conn_res, 404) # follow non existing user conn_res = post(conn, "/api/v1/accounts/doesntexist/follow") assert %{"error" => "Record not found"} = json_response(conn_res, 404) # follow non existing user via uri conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"}) assert %{"error" => "Record not found"} = json_response(conn_res, 404) # unfollow non existing user conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow") assert %{"error" => "Record not found"} = json_response(conn_res, 404) end end describe "mute/unmute" do test "with notifications", %{conn: conn} do user = insert(:user) other_user = insert(:user) conn = conn |> assign(:user, user) |> post("/api/v1/accounts/#{other_user.id}/mute") response = json_response(conn, 200) assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response user = User.get_cached_by_id(user.id) conn = build_conn() |> assign(:user, user) |> post("/api/v1/accounts/#{other_user.id}/unmute") response = json_response(conn, 200) assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response end test "without notifications", %{conn: conn} do user = insert(:user) other_user = insert(:user) conn = conn |> assign(:user, user) |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"}) response = json_response(conn, 200) assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response user = User.get_cached_by_id(user.id) conn = build_conn() |> assign(:user, user) |> post("/api/v1/accounts/#{other_user.id}/unmute") response = json_response(conn, 200) assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response end end describe "pinned statuses" do setup do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) [user: user, activity: activity] end test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do {:ok, _} = CommonAPI.pin(activity.id, user) result = conn |> assign(:user, user) |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") |> json_response(200) id_str = to_string(activity.id) assert [%{"id" => ^id_str, "pinned" => true}] = result end end test "blocking / unblocking a user", %{conn: conn} do user = insert(:user) other_user = insert(:user) conn = conn |> assign(:user, user) |> post("/api/v1/accounts/#{other_user.id}/block") assert %{"id" => _id, "blocking" => true} = json_response(conn, 200) user = User.get_cached_by_id(user.id) conn = build_conn() |> assign(:user, user) |> post("/api/v1/accounts/#{other_user.id}/unblock") assert %{"id" => _id, "blocking" => false} = json_response(conn, 200) end end