Merge branch 'features/add-credo-to-ci' into 'develop'
[akkoma] / test / web / mastodon_api / mastodon_api_controller_test.exs
index d4e2acae3f9e63406fa3e8da28ede0d1bd998351..059d5237d678b496d42b332767b396834fb42ee8 100644 (file)
@@ -5,12 +5,17 @@
 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
   use Pleroma.Web.ConnCase
 
-  alias Pleroma.Web.TwitterAPI.TwitterAPI
-  alias Pleroma.{Repo, User, Object, Activity, Notification}
-  alias Pleroma.Web.{OStatus, CommonAPI}
+  alias Ecto.Changeset
+  alias Pleroma.Activity
+  alias Pleroma.Notification
+  alias Pleroma.Object
+  alias Pleroma.Repo
+  alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.MastodonAPI.FilterView
-  alias Ecto.Changeset
+  alias Pleroma.Web.OStatus
+  alias Pleroma.Web.TwitterAPI.TwitterAPI
   import Pleroma.Factory
   import ExUnit.CaptureLog
   import Tesla.Mock
@@ -31,7 +36,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
       |> assign(:user, user)
       |> get("/api/v1/timelines/home")
 
-    assert length(json_response(conn, 200)) == 0
+    assert Enum.empty?(json_response(conn, 200))
 
     {:ok, user} = User.follow(user, following)
 
@@ -243,6 +248,33 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     assert status["url"] != direct.data["id"]
   end
 
+  test "doesn't include DMs from blocked users", %{conn: conn} do
+    blocker = insert(:user)
+    blocked = insert(:user)
+    user = insert(:user)
+    {:ok, blocker} = User.block(blocker, blocked)
+
+    {:ok, _blocked_direct} =
+      CommonAPI.post(blocked, %{
+        "status" => "Hi @#{blocker.nickname}!",
+        "visibility" => "direct"
+      })
+
+    {:ok, direct} =
+      CommonAPI.post(user, %{
+        "status" => "Hi @#{blocker.nickname}!",
+        "visibility" => "direct"
+      })
+
+    res_conn =
+      conn
+      |> assign(:user, user)
+      |> get("api/v1/timelines/direct")
+
+    [status] = json_response(res_conn, 200)
+    assert status["id"] == direct.id
+  end
+
   test "replying to a status", %{conn: conn} do
     user = insert(:user)
 
@@ -339,6 +371,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
 
       assert Repo.get(Activity, activity.id) == activity
     end
+
+    test "when you're an admin or moderator", %{conn: conn} do
+      activity1 = insert(:note_activity)
+      activity2 = insert(:note_activity)
+      admin = insert(:user, info: %{is_admin: true})
+      moderator = insert(:user, info: %{is_moderator: true})
+
+      res_conn =
+        conn
+        |> assign(:user, admin)
+        |> delete("/api/v1/statuses/#{activity1.id}")
+
+      assert %{} = json_response(res_conn, 200)
+
+      res_conn =
+        conn
+        |> assign(:user, moderator)
+        |> delete("/api/v1/statuses/#{activity2.id}")
+
+      assert %{} = json_response(res_conn, 200)
+
+      refute Repo.get(Activity, activity1.id)
+      refute Repo.get(Activity, activity2.id)
+    end
   end
 
   describe "filters" do
@@ -932,7 +988,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     end
 
     test "/api/v1/follow_requests/:id/authorize works" do
-      user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
+      user = insert(:user, %{info: %User.Info{locked: true}})
       other_user = insert(:user)
 
       {:ok, _activity} = ActivityPub.follow(other_user, user)
@@ -974,6 +1030,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
 
       {:ok, _activity} = ActivityPub.follow(other_user, user)
 
+      user = Repo.get(User, user.id)
+
       conn =
         build_conn()
         |> assign(:user, user)
@@ -1006,6 +1064,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     assert %{"error" => "Can't find user"} = json_response(conn, 404)
   end
 
+  test "account fetching also works nickname", %{conn: conn} do
+    user = insert(:user)
+
+    conn =
+      conn
+      |> get("/api/v1/accounts/#{user.nickname}")
+
+    assert %{"id" => id} = json_response(conn, 200)
+    assert id == user.id
+  end
+
   test "media upload", %{conn: conn} do
     file = %Plug.Upload{
       content_type: "image/jpg",
@@ -1126,6 +1195,47 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     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/since_id=#{follower2.id}/
+    assert link_header =~ ~r/max_id=#{follower2.id}/
+  end
+
   test "getting following", %{conn: conn} do
     user = insert(:user)
     other_user = insert(:user)
@@ -1139,8 +1249,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     assert id == to_string(other_user.id)
   end
 
-  test "getting following, hide_followings", %{conn: conn} do
-    user = insert(:user, %{info: %{hide_followings: true}})
+  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)
 
@@ -1151,8 +1261,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     assert [] == json_response(conn, 200)
   end
 
-  test "getting following, hide_followings, same user requesting", %{conn: conn} do
-    user = insert(:user, %{info: %{hide_followings: true}})
+  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)
 
@@ -1164,6 +1274,47 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     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/since_id=#{following2.id}/
+    assert link_header =~ ~r/max_id=#{following2.id}/
+  end
+
   test "following / unfollowing a user", %{conn: conn} do
     user = insert(:user)
     other_user = insert(:user)
@@ -1195,6 +1346,42 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     assert id == to_string(other_user.id)
   end
 
+  test "muting / unmuting a user", %{conn: conn} do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    conn =
+      conn
+      |> assign(:user, user)
+      |> post("/api/v1/accounts/#{other_user.id}/mute")
+
+    assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
+
+    user = Repo.get(User, user.id)
+
+    conn =
+      build_conn()
+      |> assign(:user, user)
+      |> post("/api/v1/accounts/#{other_user.id}/unmute")
+
+    assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
+  end
+
+  test "getting a list of mutes", %{conn: conn} do
+    user = insert(:user)
+    other_user = insert(:user)
+
+    {:ok, user} = User.mute(user, other_user)
+
+    conn =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/mutes")
+
+    other_user_id = to_string(other_user.id)
+    assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
+  end
+
   test "blocking / unblocking a user", %{conn: conn} do
     user = insert(:user)
     other_user = insert(:user)
@@ -1271,26 +1458,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     assert "even.worse.site" in domain_blocks
   end
 
-  test "unimplemented mute endpoints" do
+  test "unimplemented follow_requests, blocks, domain blocks" do
     user = insert(:user)
-    other_user = insert(:user)
 
-    ["mute", "unmute"]
-    |> Enum.each(fn endpoint ->
-      conn =
-        build_conn()
-        |> assign(:user, user)
-        |> post("/api/v1/accounts/#{other_user.id}/#{endpoint}")
-
-      assert %{"id" => id} = json_response(conn, 200)
-      assert id == to_string(other_user.id)
-    end)
-  end
-
-  test "unimplemented mutes, follow_requests, blocks, domain blocks" do
-    user = insert(:user)
-
-    ["blocks", "domain_blocks", "mutes", "follow_requests"]
+    ["blocks", "domain_blocks", "follow_requests"]
     |> Enum.each(fn endpoint ->
       conn =
         build_conn()
@@ -1461,9 +1632,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
       assert user = json_response(conn, 200)
 
       assert user["note"] ==
-               "I drink <a class=\"hashtag\" data-tag=\"cofe\" href=\"http://localhost:4001/tag/cofe\">#cofe</a> with <span class=\"h-card\"><a data-user=\"#{
-                 user2.id
-               }\" class=\"u-url mention\" href=\"#{user2.ap_id}\">@<span>#{user2.nickname}</span></a></span>"
+               ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a data-user=") <>
+                 user2.id <>
+                 ~s(" class="u-url mention" href=") <>
+                 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
     end
 
     test "updates the user's locking status", %{conn: conn} do
@@ -1525,6 +1697,24 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
       assert user_response = json_response(conn, 200)
       assert user_response["header"] != User.banner_url(user)
     end
+
+    test "requires 'write' permission", %{conn: conn} do
+      token1 = insert(:oauth_token, scopes: ["read"])
+      token2 = insert(:oauth_token, scopes: ["write", "follow"])
+
+      for token <- [token1, token2] do
+        conn =
+          conn
+          |> put_req_header("authorization", "Bearer #{token.token}")
+          |> patch("/api/v1/accounts/update_credentials", %{})
+
+        if token == token1 do
+          assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
+        else
+          assert json_response(conn, 200)
+        end
+      end
+    end
   end
 
   test "get instance information", %{conn: conn} do
@@ -1695,6 +1885,18 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
                }
              }
 
+      # works with private posts
+      {:ok, activity} =
+        CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
+
+      response_two =
+        conn
+        |> assign(:user, user)
+        |> get("/api/v1/statuses/#{activity.id}/card")
+        |> json_response(200)
+
+      assert response_two == response
+
       Pleroma.Config.put([:rich_media, :enabled], false)
     end
   end
@@ -1749,4 +1951,158 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
 
     assert [json_response(response2, 200)] == json_response(bookmarks, 200)
   end
+
+  describe "conversation muting" do
+    setup do
+      user = insert(:user)
+      {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
+
+      [user: user, activity: activity]
+    end
+
+    test "mute conversation", %{conn: conn, user: user, activity: activity} do
+      id_str = to_string(activity.id)
+
+      assert %{"id" => ^id_str, "muted" => true} =
+               conn
+               |> assign(:user, user)
+               |> post("/api/v1/statuses/#{activity.id}/mute")
+               |> json_response(200)
+    end
+
+    test "unmute conversation", %{conn: conn, user: user, activity: activity} do
+      {:ok, _} = CommonAPI.add_mute(user, activity)
+
+      id_str = to_string(activity.id)
+      user = refresh_record(user)
+
+      assert %{"id" => ^id_str, "muted" => false} =
+               conn
+               |> assign(:user, user)
+               |> post("/api/v1/statuses/#{activity.id}/unmute")
+               |> json_response(200)
+    end
+  end
+
+  test "flavours switching (Pleroma Extension)", %{conn: conn} do
+    user = insert(:user)
+
+    get_old_flavour =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/pleroma/flavour")
+
+    assert "glitch" == json_response(get_old_flavour, 200)
+
+    set_flavour =
+      conn
+      |> assign(:user, user)
+      |> post("/api/v1/pleroma/flavour/vanilla")
+
+    assert "vanilla" == json_response(set_flavour, 200)
+
+    get_new_flavour =
+      conn
+      |> assign(:user, user)
+      |> post("/api/v1/pleroma/flavour/vanilla")
+
+    assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
+  end
+
+  describe "reports" do
+    setup do
+      reporter = insert(:user)
+      target_user = insert(:user)
+
+      {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
+
+      [reporter: reporter, target_user: target_user, activity: activity]
+    end
+
+    test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
+      assert %{"action_taken" => false, "id" => _} =
+               conn
+               |> assign(:user, reporter)
+               |> post("/api/v1/reports", %{"account_id" => target_user.id})
+               |> json_response(200)
+    end
+
+    test "submit a report with statuses and comment", %{
+      conn: conn,
+      reporter: reporter,
+      target_user: target_user,
+      activity: activity
+    } do
+      assert %{"action_taken" => false, "id" => _} =
+               conn
+               |> assign(:user, reporter)
+               |> post("/api/v1/reports", %{
+                 "account_id" => target_user.id,
+                 "status_ids" => [activity.id],
+                 "comment" => "bad status!"
+               })
+               |> json_response(200)
+    end
+
+    test "account_id is required", %{
+      conn: conn,
+      reporter: reporter,
+      activity: activity
+    } do
+      assert %{"error" => "Valid `account_id` required"} =
+               conn
+               |> assign(:user, reporter)
+               |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
+               |> json_response(400)
+    end
+
+    test "comment must be up to the size specified in the config", %{
+      conn: conn,
+      reporter: reporter,
+      target_user: target_user
+    } do
+      max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
+      comment = String.pad_trailing("a", max_size + 1, "a")
+
+      error = %{"error" => "Comment must be up to #{max_size} characters"}
+
+      assert ^error =
+               conn
+               |> assign(:user, reporter)
+               |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
+               |> json_response(400)
+    end
+  end
+
+  describe "link headers" do
+    test "preserves parameters in link headers", %{conn: conn} do
+      user = insert(:user)
+      other_user = insert(:user)
+
+      {:ok, activity1} =
+        CommonAPI.post(other_user, %{
+          "status" => "hi @#{user.nickname}",
+          "visibility" => "public"
+        })
+
+      {:ok, activity2} =
+        CommonAPI.post(other_user, %{
+          "status" => "hi @#{user.nickname}",
+          "visibility" => "public"
+        })
+
+      notification1 = Repo.get_by(Notification, activity_id: activity1.id)
+      notification2 = Repo.get_by(Notification, activity_id: activity2.id)
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> get("/api/v1/notifications", %{media_only: true})
+
+      assert [link_header] = get_resp_header(conn, "link")
+      assert link_header =~ ~r/media_only=true/
+      assert link_header =~ ~r/since_id=#{notification2.id}/
+      assert link_header =~ ~r/max_id=#{notification1.id}/
+    end
+  end
 end