Merge branch 'captcha' into 'develop'
[akkoma] / test / web / twitter_api / twitter_api_controller_test.exs
index 2cae8a0711c543706845d85fd2d59b0ec0143005..c41f615ac261ed6dcb22e9ac61f1451d15875e50 100644 (file)
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
 defmodule Pleroma.Web.TwitterAPI.ControllerTest do
   use Pleroma.Web.ConnCase
   alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter
@@ -9,21 +13,23 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.TwitterAPI.TwitterAPI
   alias Comeonin.Pbkdf2
+  alias Ecto.Changeset
 
   import Pleroma.Factory
 
+  @banner "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
+
   describe "POST /api/account/update_profile_banner" do
     test "it updates the banner", %{conn: conn} do
       user = insert(:user)
 
-      new_banner =
-        "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
+      conn
+      |> assign(:user, user)
+      |> post(authenticated_twitter_api__path(conn, :update_banner), %{"banner" => @banner})
+      |> json_response(200)
 
-      response =
-        conn
-        |> assign(:user, user)
-        |> post(authenticated_twitter_api__path(conn, :update_banner), %{"banner" => new_banner})
-        |> json_response(200)
+      user = refresh_record(user)
+      assert user.info.banner["type"] == "Image"
     end
   end
 
@@ -31,14 +37,13 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
     test "it updates the background", %{conn: conn} do
       user = insert(:user)
 
-      new_bg =
-        "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
+      conn
+      |> assign(:user, user)
+      |> post(authenticated_twitter_api__path(conn, :update_background), %{"img" => @banner})
+      |> json_response(200)
 
-      response =
-        conn
-        |> assign(:user, user)
-        |> post(authenticated_twitter_api__path(conn, :update_background), %{"img" => new_bg})
-        |> json_response(200)
+      user = refresh_record(user)
+      assert user.info.background["type"] == "Image"
     end
   end
 
@@ -51,12 +56,12 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
     end
 
     test "with credentials", %{conn: conn, user: user} do
-      conn =
+      response =
         conn
         |> with_credentials(user.nickname, "test")
         |> post("/api/account/verify_credentials.json")
+        |> json_response(200)
 
-      assert response = json_response(conn, 200)
       assert response == UserView.render("show.json", %{user: user, token: response["token"]})
     end
   end
@@ -78,17 +83,28 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
         "error" => "Client must provide a 'status' parameter with a value."
       }
 
-      conn = conn_with_creds |> post(request_path)
+      conn =
+        conn_with_creds
+        |> post(request_path)
+
       assert json_response(conn, 400) == error_response
 
-      conn = conn_with_creds |> post(request_path, %{status: ""})
+      conn =
+        conn_with_creds
+        |> post(request_path, %{status: ""})
+
       assert json_response(conn, 400) == error_response
 
-      conn = conn_with_creds |> post(request_path, %{status: " "})
+      conn =
+        conn_with_creds
+        |> post(request_path, %{status: " "})
+
       assert json_response(conn, 400) == error_response
 
       # we post with visibility private in order to avoid triggering relay
-      conn = conn_with_creds |> post(request_path, %{status: "Nice meme.", visibility: "private"})
+      conn =
+        conn_with_creds
+        |> post(request_path, %{status: "Nice meme.", visibility: "private"})
 
       assert json_response(conn, 200) ==
                ActivityRepresenter.to_map(Repo.one(Activity), %{user: user})
@@ -96,6 +112,8 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
   end
 
   describe "GET /statuses/public_timeline.json" do
+    setup [:valid_user]
+
     test "returns statuses", %{conn: conn} do
       user = insert(:user)
       activities = ActivityBuilder.insert_list(30, %{}, %{user: user})
@@ -111,7 +129,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       assert length(response) == 10
     end
 
-    test "returns 403 to unauthenticated request when the instance is not public" do
+    test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do
       instance =
         Application.get_env(:pleroma, :instance)
         |> Keyword.put(:public, false)
@@ -129,15 +147,45 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       Application.put_env(:pleroma, :instance, instance)
     end
 
-    test "returns 200 to unauthenticated request when the instance is public" do
+    test "returns 200 to authenticated request when the instance is not public",
+         %{conn: conn, user: user} do
+      instance =
+        Application.get_env(:pleroma, :instance)
+        |> Keyword.put(:public, false)
+
+      Application.put_env(:pleroma, :instance, instance)
+
+      conn
+      |> with_credentials(user.nickname, "test")
+      |> get("/api/statuses/public_timeline.json")
+      |> json_response(200)
+
+      instance =
+        Application.get_env(:pleroma, :instance)
+        |> Keyword.put(:public, true)
+
+      Application.put_env(:pleroma, :instance, instance)
+    end
+
+    test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do
       conn
       |> get("/api/statuses/public_timeline.json")
       |> json_response(200)
     end
+
+    test "returns 200 to authenticated request when the instance is public",
+         %{conn: conn, user: user} do
+      conn
+      |> with_credentials(user.nickname, "test")
+      |> get("/api/statuses/public_timeline.json")
+      |> json_response(200)
+    end
   end
 
   describe "GET /statuses/public_and_external_timeline.json" do
-    test "returns 403 to unauthenticated request when the instance is not public" do
+    setup [:valid_user]
+
+    test "returns 403 to unauthenticated request when the instance is not public", %{conn: conn} do
       instance =
         Application.get_env(:pleroma, :instance)
         |> Keyword.put(:public, false)
@@ -155,8 +203,36 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       Application.put_env(:pleroma, :instance, instance)
     end
 
-    test "returns 200 to unauthenticated request when the instance is public" do
+    test "returns 200 to authenticated request when the instance is not public",
+         %{conn: conn, user: user} do
+      instance =
+        Application.get_env(:pleroma, :instance)
+        |> Keyword.put(:public, false)
+
+      Application.put_env(:pleroma, :instance, instance)
+
+      conn
+      |> with_credentials(user.nickname, "test")
+      |> get("/api/statuses/public_and_external_timeline.json")
+      |> json_response(200)
+
+      instance =
+        Application.get_env(:pleroma, :instance)
+        |> Keyword.put(:public, true)
+
+      Application.put_env(:pleroma, :instance, instance)
+    end
+
+    test "returns 200 to unauthenticated request when the instance is public", %{conn: conn} do
+      conn
+      |> get("/api/statuses/public_and_external_timeline.json")
+      |> json_response(200)
+    end
+
+    test "returns 200 to authenticated request when the instance is public",
+         %{conn: conn, user: user} do
       conn
+      |> with_credentials(user.nickname, "test")
       |> get("/api/statuses/public_and_external_timeline.json")
       |> json_response(200)
     end
@@ -259,7 +335,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       since_id = List.last(activities).id
 
       current_user =
-        Ecto.Changeset.change(current_user, following: [User.ap_followers(user)])
+        Changeset.change(current_user, following: [User.ap_followers(user)])
         |> Repo.update!()
 
       conn =
@@ -503,6 +579,34 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       assert length(response) == 1
       assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
     end
+
+    test "with credentials with user_id, excluding RTs", %{conn: conn, user: current_user} do
+      user = insert(:user)
+      {:ok, activity} = ActivityBuilder.insert(%{"id" => 1, "type" => "Create"}, %{user: user})
+      {:ok, _} = ActivityBuilder.insert(%{"id" => 2, "type" => "Announce"}, %{user: user})
+
+      conn =
+        conn
+        |> with_credentials(current_user.nickname, "test")
+        |> get("/api/statuses/user_timeline.json", %{
+          "user_id" => user.id,
+          "include_rts" => "false"
+        })
+
+      response = json_response(conn, 200)
+
+      assert length(response) == 1
+      assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
+
+      conn =
+        conn
+        |> get("/api/statuses/user_timeline.json", %{"user_id" => user.id, "include_rts" => "0"})
+
+      response = json_response(conn, 200)
+
+      assert length(response) == 1
+      assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
+    end
   end
 
   describe "POST /friendships/create.json" do
@@ -648,14 +752,13 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
     end
 
     test "unimplemented mutes with credentials", %{conn: conn, user: current_user} do
-      conn =
+      response =
         conn
         |> with_credentials(current_user.nickname, "test")
         |> get("/api/qvitter/mutes.json")
+        |> json_response(200)
 
-      current_user = Repo.get(User, current_user.id)
-
-      assert [] = json_response(conn, 200)
+      assert [] = response
     end
   end
 
@@ -822,6 +925,129 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
     end
   end
 
+  describe "POST /api/account/password_reset, with valid parameters" do
+    setup %{conn: conn} do
+      user = insert(:user)
+      conn = post(conn, "/api/account/password_reset?email=#{user.email}")
+      %{conn: conn, user: user}
+    end
+
+    test "it returns 204", %{conn: conn} do
+      assert json_response(conn, :no_content)
+    end
+
+    test "it creates a PasswordResetToken record for user", %{user: user} do
+      token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
+      assert token_record
+    end
+
+    test "it sends an email to user", %{user: user} do
+      token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
+
+      Swoosh.TestAssertions.assert_email_sent(
+        Pleroma.UserEmail.password_reset_email(user, token_record.token)
+      )
+    end
+  end
+
+  describe "POST /api/account/password_reset, with invalid parameters" do
+    setup [:valid_user]
+
+    test "it returns 500 when user is not found", %{conn: conn, user: user} do
+      conn = post(conn, "/api/account/password_reset?email=nonexisting_#{user.email}")
+      assert json_response(conn, :internal_server_error)
+    end
+
+    test "it returns 500 when user is not local", %{conn: conn, user: user} do
+      {:ok, user} = Repo.update(Changeset.change(user, local: false))
+      conn = post(conn, "/api/account/password_reset?email=#{user.email}")
+      assert json_response(conn, :internal_server_error)
+    end
+  end
+
+  describe "GET /api/account/confirm_email/:id/:token" do
+    setup do
+      user = insert(:user)
+      info_change = User.Info.confirmation_changeset(user.info, :unconfirmed)
+
+      {:ok, user} =
+        user
+        |> Changeset.change()
+        |> Changeset.put_embed(:info, info_change)
+        |> Repo.update()
+
+      assert user.info.confirmation_pending
+
+      [user: user]
+    end
+
+    test "it redirects to root url", %{conn: conn, user: user} do
+      conn = get(conn, "/api/account/confirm_email/#{user.id}/#{user.info.confirmation_token}")
+
+      assert 302 == conn.status
+    end
+
+    test "it confirms the user account", %{conn: conn, user: user} do
+      get(conn, "/api/account/confirm_email/#{user.id}/#{user.info.confirmation_token}")
+
+      user = Repo.get(User, user.id)
+
+      refute user.info.confirmation_pending
+      refute user.info.confirmation_token
+    end
+
+    test "it returns 500 if user cannot be found by id", %{conn: conn, user: user} do
+      conn = get(conn, "/api/account/confirm_email/0/#{user.info.confirmation_token}")
+
+      assert 500 == conn.status
+    end
+
+    test "it returns 500 if token is invalid", %{conn: conn, user: user} do
+      conn = get(conn, "/api/account/confirm_email/#{user.id}/wrong_token")
+
+      assert 500 == conn.status
+    end
+  end
+
+  describe "POST /api/account/resend_confirmation_email" do
+    setup do
+      setting = Pleroma.Config.get([:instance, :account_activation_required])
+
+      unless setting do
+        Pleroma.Config.put([:instance, :account_activation_required], true)
+        on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
+      end
+
+      user = insert(:user)
+      info_change = User.Info.confirmation_changeset(user.info, :unconfirmed)
+
+      {:ok, user} =
+        user
+        |> Changeset.change()
+        |> Changeset.put_embed(:info, info_change)
+        |> Repo.update()
+
+      assert user.info.confirmation_pending
+
+      [user: user]
+    end
+
+    test "it returns 204 No Content", %{conn: conn, user: user} do
+      conn
+      |> assign(:user, user)
+      |> post("/api/account/resend_confirmation_email?email=#{user.email}")
+      |> json_response(:no_content)
+    end
+
+    test "it sends confirmation email", %{conn: conn, user: user} do
+      conn
+      |> assign(:user, user)
+      |> post("/api/account/resend_confirmation_email?email=#{user.email}")
+
+      Swoosh.TestAssertions.assert_email_sent(Pleroma.UserEmail.account_confirmation_email(user))
+    end
+  end
+
   describe "GET /api/externalprofile/show" do
     test "it returns the user", %{conn: conn} do
       user = insert(:user)
@@ -855,6 +1081,86 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       result = json_response(conn, 200)
       assert Enum.sort(expected) == Enum.sort(result)
     end
+
+    test "it returns a given user's followers with user_id", %{conn: conn} do
+      user = insert(:user)
+      follower_one = insert(:user)
+      follower_two = insert(:user)
+      not_follower = insert(:user)
+
+      {:ok, follower_one} = User.follow(follower_one, user)
+      {:ok, follower_two} = User.follow(follower_two, user)
+
+      conn =
+        conn
+        |> assign(:user, not_follower)
+        |> get("/api/statuses/followers", %{"user_id" => user.id})
+
+      assert MapSet.equal?(
+               MapSet.new(json_response(conn, 200)),
+               MapSet.new(
+                 UserView.render("index.json", %{
+                   users: [follower_one, follower_two],
+                   for: not_follower
+                 })
+               )
+             )
+    end
+
+    test "it returns empty for a hidden network", %{conn: conn} do
+      user = insert(:user, %{info: %{hide_network: true}})
+      follower_one = insert(:user)
+      follower_two = insert(:user)
+      not_follower = insert(:user)
+
+      {:ok, _follower_one} = User.follow(follower_one, user)
+      {:ok, _follower_two} = User.follow(follower_two, user)
+
+      response =
+        conn
+        |> assign(:user, not_follower)
+        |> get("/api/statuses/followers", %{"user_id" => user.id})
+        |> json_response(200)
+
+      assert [] == response
+    end
+
+    test "it returns the followers for a hidden network if requested by the user themselves", %{
+      conn: conn
+    } do
+      user = insert(:user, %{info: %{hide_network: true}})
+      follower_one = insert(:user)
+      follower_two = insert(:user)
+      _not_follower = insert(:user)
+
+      {:ok, _follower_one} = User.follow(follower_one, user)
+      {:ok, _follower_two} = User.follow(follower_two, user)
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> get("/api/statuses/followers", %{"user_id" => user.id})
+
+      refute [] == json_response(conn, 200)
+    end
+  end
+
+  describe "GET /api/statuses/blocks" do
+    test "it returns the list of users blocked by requester", %{conn: conn} do
+      user = insert(:user)
+      other_user = insert(:user)
+
+      {:ok, user} = User.block(user, other_user)
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> get("/api/statuses/blocks")
+
+      expected = UserView.render("index.json", %{users: [other_user], for: user})
+      result = json_response(conn, 200)
+      assert Enum.sort(expected) == Enum.sort(result)
+    end
   end
 
   describe "GET /api/statuses/friends" do
@@ -899,6 +1205,43 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
              )
     end
 
+    test "it returns empty for a hidden network", %{conn: conn} do
+      user = insert(:user, %{info: %{hide_network: true}})
+      followed_one = insert(:user)
+      followed_two = insert(:user)
+      not_followed = insert(:user)
+
+      {:ok, user} = User.follow(user, followed_one)
+      {:ok, user} = User.follow(user, followed_two)
+
+      conn =
+        conn
+        |> assign(:user, not_followed)
+        |> get("/api/statuses/friends", %{"user_id" => user.id})
+
+      assert [] == json_response(conn, 200)
+    end
+
+    test "it returns friends for a hidden network if the user themselves request it", %{
+      conn: conn
+    } do
+      user = insert(:user, %{info: %{hide_network: true}})
+      followed_one = insert(:user)
+      followed_two = insert(:user)
+      _not_followed = insert(:user)
+
+      {:ok, _user} = User.follow(user, followed_one)
+      {:ok, _user} = User.follow(user, followed_two)
+
+      response =
+        conn
+        |> assign(:user, user)
+        |> get("/api/statuses/friends", %{"user_id" => user.id})
+        |> json_response(200)
+
+      refute [] == response
+    end
+
     test "it returns a given user's friends with screen_name", %{conn: conn} do
       user = insert(:user)
       followed_one = insert(:user)
@@ -963,8 +1306,34 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       assert user.name == "new name"
 
       assert user.bio ==
-               "hi <span><a class='mention' href='#{user2.ap_id}'>@<span>#{user2.nickname}</span></a></span>"
+               "hi <span><a data-user='#{user2.id}' class='mention' href='#{user2.ap_id}'>@<span>#{
+                 user2.nickname
+               }</span></a></span>"
+
+      assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user})
+    end
+
+    test "it sets and un-sets hide_network", %{conn: conn} do
+      user = insert(:user)
+
+      conn
+      |> assign(:user, user)
+      |> post("/api/account/update_profile.json", %{
+        "hide_network" => "true"
+      })
+
+      user = Repo.get!(User, user.id)
+      assert user.info.hide_network == true
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> post("/api/account/update_profile.json", %{
+          "hide_network" => "false"
+        })
 
+      user = Repo.get!(User, user.id)
+      assert user.info.hide_network == false
       assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user})
     end
 
@@ -1247,4 +1616,82 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
       assert [user.id, user_two.id, user_three.id] == Enum.map(resp, fn %{"id" => id} -> id end)
     end
   end
+
+  describe "POST /api/media/upload" do
+    setup context do
+      Pleroma.DataCase.ensure_local_uploader(context)
+    end
+
+    test "it performs the upload and sets `data[actor]` with AP id of uploader user", %{
+      conn: conn
+    } do
+      user = insert(:user)
+
+      upload_filename = "test/fixtures/image_tmp.jpg"
+      File.cp!("test/fixtures/image.jpg", upload_filename)
+
+      file = %Plug.Upload{
+        content_type: "image/jpg",
+        path: Path.absname(upload_filename),
+        filename: "image.jpg"
+      }
+
+      response =
+        conn
+        |> assign(:user, user)
+        |> put_req_header("content-type", "application/octet-stream")
+        |> post("/api/media/upload", %{
+          "media" => file
+        })
+        |> json_response(:ok)
+
+      assert response["media_id"]
+      object = Repo.get(Object, response["media_id"])
+      assert object
+      assert object.data["actor"] == User.ap_id(user)
+    end
+  end
+
+  describe "POST /api/media/metadata/create" do
+    setup do
+      object = insert(:note)
+      user = User.get_by_ap_id(object.data["actor"])
+      %{object: object, user: user}
+    end
+
+    test "it returns :forbidden status on attempt to modify someone else's upload", %{
+      conn: conn,
+      object: object
+    } do
+      initial_description = object.data["name"]
+      another_user = insert(:user)
+
+      conn
+      |> assign(:user, another_user)
+      |> post("/api/media/metadata/create", %{"media_id" => object.id})
+      |> json_response(:forbidden)
+
+      object = Repo.get(Object, object.id)
+      assert object.data["name"] == initial_description
+    end
+
+    test "it updates `data[name]` of referenced Object with provided value", %{
+      conn: conn,
+      object: object,
+      user: user
+    } do
+      description = "Informative description of the image. Initial value: #{object.data["name"]}}"
+
+      conn
+      |> assign(:user, user)
+      |> post("/api/media/metadata/create", %{
+        "media_id" => object.id,
+        "alt_text" => %{"text" => description}
+      })
+      |> json_response(:no_content)
+
+      object = Repo.get(Object, object.id)
+      assert object.data["name"] == description
+    end
+  end
 end