Merge branch 'develop' into feature/bulk-confirmation
[akkoma] / test / web / mastodon_api / controllers / status_controller_test.exs
index 962e64b03ce22d582bc58c7570bb98141329e8d5..633a25e506b1ee3cdf779bcf81d8423956966424 100644 (file)
@@ -4,9 +4,9 @@
 
 defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
   use Pleroma.Web.ConnCase
+  use Oban.Testing, repo: Pleroma.Repo
 
   alias Pleroma.Activity
-  alias Pleroma.ActivityExpiration
   alias Pleroma.Config
   alias Pleroma.Conversation.Participation
   alias Pleroma.Object
@@ -22,13 +22,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
   setup do: clear_config([:instance, :federating])
   setup do: clear_config([:instance, :allow_relay])
   setup do: clear_config([:rich_media, :enabled])
+  setup do: clear_config([:mrf, :policies])
+  setup do: clear_config([:mrf_keyword, :reject])
 
   describe "posting statuses" do
     setup do: oauth_access(["write:statuses"])
 
     test "posting a status does not increment reblog_count when relaying", %{conn: conn} do
-      Pleroma.Config.put([:instance, :federating], true)
-      Pleroma.Config.get([:instance, :allow_relay], true)
+      Config.put([:instance, :federating], true)
+      Config.get([:instance, :allow_relay], true)
 
       response =
         conn
@@ -101,7 +103,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
 
       # An activity that will expire:
       # 2 hours
-      expires_in = 120 * 60
+      expires_in = 2 * 60 * 60
+
+      expires_at = DateTime.add(DateTime.utc_now(), expires_in)
 
       conn_four =
         conn
@@ -111,29 +115,22 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
           "expires_in" => expires_in
         })
 
-      assert fourth_response =
-               %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200)
-
-      assert activity = Activity.get_by_id(fourth_id)
-      assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
-
-      estimated_expires_at =
-        NaiveDateTime.utc_now()
-        |> NaiveDateTime.add(expires_in)
-        |> NaiveDateTime.truncate(:second)
+      assert %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200)
 
-      # This assert will fail if the test takes longer than a minute. I sure hope it never does:
-      assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
+      assert Activity.get_by_id(fourth_id)
 
-      assert fourth_response["pleroma"]["expires_at"] ==
-               NaiveDateTime.to_iso8601(expiration.scheduled_at)
+      assert_enqueued(
+        worker: Pleroma.Workers.PurgeExpiredActivity,
+        args: %{activity_id: fourth_id},
+        scheduled_at: expires_at
+      )
     end
 
     test "it fails to create a status if `expires_in` is less or equal than an hour", %{
       conn: conn
     } do
-      # 1 hour
-      expires_in = 60 * 60
+      # 1 minute
+      expires_in = 1 * 60
 
       assert %{"error" => "Expiry date is too soon"} =
                conn
@@ -144,8 +141,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
                })
                |> json_response_and_validate_schema(422)
 
-      # 30 minutes
-      expires_in = 30 * 60
+      # 5 minutes
+      expires_in = 5 * 60
 
       assert %{"error" => "Expiry date is too soon"} =
                conn
@@ -157,6 +154,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
                |> json_response_and_validate_schema(422)
     end
 
+    test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do
+      Config.put([:mrf_keyword, :reject], ["GNO"])
+      Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
+
+      assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} =
+               conn
+               |> put_req_header("content-type", "application/json")
+               |> post("api/v1/statuses", %{"status" => "GNO/Linux"})
+               |> json_response_and_validate_schema(422)
+    end
+
     test "posting an undefined status with an attachment", %{user: user, conn: conn} do
       file = %Plug.Upload{
         content_type: "image/jpg",
@@ -283,9 +291,45 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
       assert real_status == fake_status
     end
 
+    test "fake statuses' preview card is not cached", %{conn: conn} do
+      clear_config([:rich_media, :enabled], true)
+
+      Tesla.Mock.mock(fn
+        %{
+          method: :get,
+          url: "https://example.com/twitter-card"
+        } ->
+          %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")}
+
+        env ->
+          apply(HttpRequestMock, :request, [env])
+      end)
+
+      conn1 =
+        conn
+        |> put_req_header("content-type", "application/json")
+        |> post("/api/v1/statuses", %{
+          "status" => "https://example.com/ogp",
+          "preview" => true
+        })
+
+      conn2 =
+        conn
+        |> put_req_header("content-type", "application/json")
+        |> post("/api/v1/statuses", %{
+          "status" => "https://example.com/twitter-card",
+          "preview" => true
+        })
+
+      assert %{"card" => %{"title" => "The Rock"}} = json_response_and_validate_schema(conn1, 200)
+
+      assert %{"card" => %{"title" => "Small Island Developing States Photo Submission"}} =
+               json_response_and_validate_schema(conn2, 200)
+    end
+
     test "posting a status with OGP link preview", %{conn: conn} do
       Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-      Config.put([:rich_media, :enabled], true)
+      clear_config([:rich_media, :enabled], true)
 
       conn =
         conn
@@ -760,13 +804,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
     test "when you created it" do
       %{user: author, conn: conn} = oauth_access(["write:statuses"])
       activity = insert(:note_activity, user: author)
+      object = Object.normalize(activity)
 
-      conn =
+      content = object.data["content"]
+      source = object.data["source"]
+
+      result =
         conn
         |> assign(:user, author)
         |> delete("/api/v1/statuses/#{activity.id}")
+        |> json_response_and_validate_schema(200)
 
-      assert %{} = json_response_and_validate_schema(conn, 200)
+      assert match?(%{"content" => ^content, "text" => ^source}, result)
 
       refute Activity.get_by_id(activity.id)
     end
@@ -789,7 +838,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
 
       conn = delete(conn, "/api/v1/statuses/#{activity.id}")
 
-      assert %{"error" => _} = json_response_and_validate_schema(conn, 403)
+      assert %{"error" => "Record not found"} == json_response_and_validate_schema(conn, 404)
 
       assert Activity.get_by_id(activity.id) == activity
     end
@@ -878,8 +927,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
       user3 = insert(:user)
       {:ok, _} = CommonAPI.favorite(user2, activity.id)
       {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
-      {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
-      {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
+      {:ok, reblog_activity1} = CommonAPI.repeat(activity.id, user1)
+      {:ok, _} = CommonAPI.repeat(activity.id, user2)
 
       conn_res =
         build_conn()
@@ -917,7 +966,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
     test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do
       activity = insert(:note_activity)
 
-      {:ok, _, _} = CommonAPI.repeat(activity.id, user)
+      {:ok, _} = CommonAPI.repeat(activity.id, user)
 
       conn =
         conn
@@ -1092,6 +1141,52 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
                |> post("/api/v1/statuses/#{activity_two.id}/pin")
                |> json_response_and_validate_schema(400)
     end
+
+    test "on pin removes deletion job, on unpin reschedule deletion" do
+      %{conn: conn} = oauth_access(["write:accounts", "write:statuses"])
+      expires_in = 2 * 60 * 60
+
+      expires_at = DateTime.add(DateTime.utc_now(), expires_in)
+
+      assert %{"id" => id} =
+               conn
+               |> put_req_header("content-type", "application/json")
+               |> post("api/v1/statuses", %{
+                 "status" => "oolong",
+                 "expires_in" => expires_in
+               })
+               |> json_response_and_validate_schema(200)
+
+      assert_enqueued(
+        worker: Pleroma.Workers.PurgeExpiredActivity,
+        args: %{activity_id: id},
+        scheduled_at: expires_at
+      )
+
+      assert %{"id" => ^id, "pinned" => true} =
+               conn
+               |> put_req_header("content-type", "application/json")
+               |> post("/api/v1/statuses/#{id}/pin")
+               |> json_response_and_validate_schema(200)
+
+      refute_enqueued(
+        worker: Pleroma.Workers.PurgeExpiredActivity,
+        args: %{activity_id: id},
+        scheduled_at: expires_at
+      )
+
+      assert %{"id" => ^id, "pinned" => false} =
+               conn
+               |> put_req_header("content-type", "application/json")
+               |> post("/api/v1/statuses/#{id}/unpin")
+               |> json_response_and_validate_schema(200)
+
+      assert_enqueued(
+        worker: Pleroma.Workers.PurgeExpiredActivity,
+        args: %{activity_id: id},
+        scheduled_at: expires_at
+      )
+    end
   end
 
   describe "cards" do
@@ -1414,6 +1509,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
       [%{"id" => id}] = response
       assert id == other_user.id
     end
+
+    test "returns empty array when :show_reactions is disabled", %{conn: conn, activity: activity} do
+      clear_config([:instance, :show_reactions], false)
+
+      other_user = insert(:user)
+      {:ok, _} = CommonAPI.favorite(other_user, activity.id)
+
+      response =
+        conn
+        |> get("/api/v1/statuses/#{activity.id}/favourited_by")
+        |> json_response_and_validate_schema(:ok)
+
+      assert Enum.empty?(response)
+    end
   end
 
   describe "GET /api/v1/statuses/:id/reblogged_by" do
@@ -1427,7 +1536,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
 
     test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
       other_user = insert(:user)
-      {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+      {:ok, _} = CommonAPI.repeat(activity.id, other_user)
 
       response =
         conn
@@ -1458,7 +1567,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
       other_user = insert(:user)
       {:ok, _user_relationship} = User.block(user, other_user)
 
-      {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+      {:ok, _} = CommonAPI.repeat(activity.id, other_user)
 
       response =
         conn
@@ -1469,12 +1578,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
     end
 
     test "does not return users who have reblogged the status privately", %{
-      conn: conn,
-      activity: activity
+      conn: conn
     } do
       other_user = insert(:user)
+      {:ok, activity} = CommonAPI.post(other_user, %{status: "my secret post"})
 
-      {:ok, _, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
+      {:ok, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
 
       response =
         conn
@@ -1486,7 +1595,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
 
     test "does not fail on an unauthenticated request", %{activity: activity} do
       other_user = insert(:user)
-      {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+      {:ok, _} = CommonAPI.repeat(activity.id, other_user)
 
       response =
         build_conn()
@@ -1541,14 +1650,49 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
            } = response
   end
 
+  test "favorites paginate correctly" do
+    %{user: user, conn: conn} = oauth_access(["read:favourites"])
+    other_user = insert(:user)
+    {:ok, first_post} = CommonAPI.post(other_user, %{status: "bla"})
+    {:ok, second_post} = CommonAPI.post(other_user, %{status: "bla"})
+    {:ok, third_post} = CommonAPI.post(other_user, %{status: "bla"})
+
+    {:ok, _first_favorite} = CommonAPI.favorite(user, third_post.id)
+    {:ok, _second_favorite} = CommonAPI.favorite(user, first_post.id)
+    {:ok, third_favorite} = CommonAPI.favorite(user, second_post.id)
+
+    result =
+      conn
+      |> get("/api/v1/favourites?limit=1")
+
+    assert [%{"id" => post_id}] = json_response_and_validate_schema(result, 200)
+    assert post_id == second_post.id
+
+    # Using the header for pagination works correctly
+    [next, _] = get_resp_header(result, "link") |> hd() |> String.split(", ")
+    [_, max_id] = Regex.run(~r/max_id=([^&]+)/, next)
+
+    assert max_id == third_favorite.id
+
+    result =
+      conn
+      |> get("/api/v1/favourites?max_id=#{max_id}")
+
+    assert [%{"id" => first_post_id}, %{"id" => third_post_id}] =
+             json_response_and_validate_schema(result, 200)
+
+    assert first_post_id == first_post.id
+    assert third_post_id == third_post.id
+  end
+
   test "returns the favorites of a user" do
     %{user: user, conn: conn} = oauth_access(["read:favourites"])
     other_user = insert(:user)
 
     {:ok, _} = CommonAPI.post(other_user, %{status: "bla"})
-    {:ok, activity} = CommonAPI.post(other_user, %{status: "traps are happy"})
+    {:ok, activity} = CommonAPI.post(other_user, %{status: "trees are happy"})
 
-    {:ok, _} = CommonAPI.favorite(user, activity.id)
+    {:ok, last_like} = CommonAPI.favorite(user, activity.id)
 
     first_conn = get(conn, "/api/v1/favourites")
 
@@ -1566,9 +1710,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
 
     {:ok, _} = CommonAPI.favorite(user, second_activity.id)
 
-    last_like = status["id"]
-
-    second_conn = get(conn, "/api/v1/favourites?since_id=#{last_like}")
+    second_conn = get(conn, "/api/v1/favourites?since_id=#{last_like.id}")
 
     assert [second_status] = json_response_and_validate_schema(second_conn, 200)
     assert second_status["id"] == to_string(second_activity.id)
@@ -1580,19 +1722,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
 
   test "expires_at is nil for another user" do
     %{conn: conn, user: user} = oauth_access(["read:statuses"])
+    expires_at = DateTime.add(DateTime.utc_now(), 1_000_000)
     {:ok, activity} = CommonAPI.post(user, %{status: "foobar", expires_in: 1_000_000})
 
-    expires_at =
-      activity.id
-      |> ActivityExpiration.get_by_activity_id()
-      |> Map.get(:scheduled_at)
-      |> NaiveDateTime.to_iso8601()
-
-    assert %{"pleroma" => %{"expires_at" => ^expires_at}} =
+    assert %{"pleroma" => %{"expires_at" => a_expires_at}} =
              conn
              |> get("/api/v1/statuses/#{activity.id}")
              |> json_response_and_validate_schema(:ok)
 
+    {:ok, a_expires_at, 0} = DateTime.from_iso8601(a_expires_at)
+    assert DateTime.diff(expires_at, a_expires_at) == 0
+
     %{conn: conn} = oauth_access(["read:statuses"])
 
     assert %{"pleroma" => %{"expires_at" => nil}} =