OpenAPI: MastodonAPI Timeline Controller
[akkoma] / test / pleroma / web / mastodon_api / controllers / timeline_controller_test.exs
index 8356b64d373088d8ee12c5ffb1cb37245e9664b0..ed1286675cdb0419719998048e5bfc07bf74f86a 100644 (file)
@@ -1,5 +1,5 @@
 # Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
@@ -90,6 +90,65 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
                }
              ] = result
     end
+
+    test "filtering", %{conn: conn, user: user} do
+      local_user = insert(:user)
+      {:ok, user, local_user} = User.follow(user, local_user)
+      {:ok, local_activity} = CommonAPI.post(local_user, %{status: "Status"})
+      with_media = create_with_media_activity(local_user)
+
+      remote_user = insert(:user, local: false)
+      {:ok, _user, remote_user} = User.follow(user, remote_user)
+      remote_activity = create_remote_activity(remote_user)
+
+      without_filter_ids =
+        conn
+        |> get("/api/v1/timelines/home")
+        |> json_response_and_validate_schema(200)
+        |> Enum.map(& &1["id"])
+
+      assert local_activity.id in without_filter_ids
+      assert remote_activity.id in without_filter_ids
+      assert with_media.id in without_filter_ids
+
+      only_local_ids =
+        conn
+        |> get("/api/v1/timelines/home?local=true")
+        |> json_response_and_validate_schema(200)
+        |> Enum.map(& &1["id"])
+
+      assert local_activity.id in only_local_ids
+      refute remote_activity.id in only_local_ids
+      assert with_media.id in only_local_ids
+
+      only_local_media_ids =
+        conn
+        |> get("/api/v1/timelines/home?local=true&only_media=true")
+        |> json_response_and_validate_schema(200)
+        |> Enum.map(& &1["id"])
+
+      refute local_activity.id in only_local_media_ids
+      refute remote_activity.id in only_local_media_ids
+      assert with_media.id in only_local_media_ids
+
+      remote_ids =
+        conn
+        |> get("/api/v1/timelines/home?remote=true")
+        |> json_response_and_validate_schema(200)
+        |> Enum.map(& &1["id"])
+
+      refute local_activity.id in remote_ids
+      assert remote_activity.id in remote_ids
+      refute with_media.id in remote_ids
+
+      assert conn
+             |> get("/api/v1/timelines/home?remote=true&only_media=true")
+             |> json_response_and_validate_schema(200) == []
+
+      assert conn
+             |> get("/api/v1/timelines/home?remote=true&local=true")
+             |> json_response_and_validate_schema(200) == []
+    end
   end
 
   describe "public" do
@@ -98,27 +157,80 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
       user = insert(:user)
 
       {:ok, activity} = CommonAPI.post(user, %{status: "test"})
+      with_media = create_with_media_activity(user)
 
-      _activity = insert(:note_activity, local: false)
+      remote = insert(:note_activity, local: false)
 
-      conn = get(conn, "/api/v1/timelines/public?local=False")
+      assert conn
+             |> get("/api/v1/timelines/public?local=False")
+             |> json_response_and_validate_schema(:ok)
+             |> length == 3
 
-      assert length(json_response_and_validate_schema(conn, :ok)) == 2
+      local_ids =
+        conn
+        |> get("/api/v1/timelines/public?local=True")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
 
-      conn = get(build_conn(), "/api/v1/timelines/public?local=True")
+      assert activity.id in local_ids
+      assert with_media.id in local_ids
+      refute remote.id in local_ids
 
-      assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
+      local_ids =
+        conn
+        |> get("/api/v1/timelines/public?local=True")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
 
-      conn = get(build_conn(), "/api/v1/timelines/public?local=1")
+      assert activity.id in local_ids
+      assert with_media.id in local_ids
+      refute remote.id in local_ids
 
-      assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
+      local_ids =
+        conn
+        |> get("/api/v1/timelines/public?local=True&only_media=true")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
+
+      refute activity.id in local_ids
+      assert with_media.id in local_ids
+      refute remote.id in local_ids
+
+      local_ids =
+        conn
+        |> get("/api/v1/timelines/public?local=1")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
+
+      assert activity.id in local_ids
+      assert with_media.id in local_ids
+      refute remote.id in local_ids
+
+      remote_id = remote.id
+
+      assert [%{"id" => ^remote_id}] =
+               conn
+               |> get("/api/v1/timelines/public?remote=true")
+               |> json_response_and_validate_schema(:ok)
+
+      with_media_id = with_media.id
+
+      assert [%{"id" => ^with_media_id}] =
+               conn
+               |> get("/api/v1/timelines/public?only_media=true")
+               |> json_response_and_validate_schema(:ok)
+
+      assert conn
+             |> get("/api/v1/timelines/public?remote=true&only_media=true")
+             |> json_response_and_validate_schema(:ok) == []
 
       # does not contain repeats
       {:ok, _} = CommonAPI.repeat(activity.id, user)
 
-      conn = get(build_conn(), "/api/v1/timelines/public?local=true")
-
-      assert [_] = json_response_and_validate_schema(conn, :ok)
+      assert [_, _] =
+               conn
+               |> get("/api/v1/timelines/public?local=true")
+               |> json_response_and_validate_schema(:ok)
     end
 
     test "the public timeline includes only public statuses for an authenticated user" do
@@ -136,7 +248,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
     test "doesn't return replies if follower is posting with blocked user" do
       %{conn: conn, user: blocker} = oauth_access(["read:statuses"])
       [blockee, friend] = insert_list(2, :user)
-      {:ok, blocker} = User.follow(blocker, friend)
+      {:ok, blocker, friend} = User.follow(blocker, friend)
       {:ok, _} = User.block(blocker, blockee)
 
       conn = assign(conn, :user, blocker)
@@ -165,7 +277,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
       %{conn: conn, user: blocker} = oauth_access(["read:statuses"])
       friend = insert(:user)
       blockee = insert(:user, ap_id: "https://example.com/users/blocked")
-      {:ok, blocker} = User.follow(blocker, friend)
+      {:ok, blocker, friend} = User.follow(blocker, friend)
       {:ok, blocker} = User.block_domain(blocker, "example.com")
 
       conn = assign(conn, :user, blocker)
@@ -336,7 +448,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
       user_one = insert(:user)
       user_two = insert(:user)
 
-      {:ok, user_two} = User.follow(user_two, user_one)
+      {:ok, user_two, user_one} = User.follow(user_two, user_one)
 
       {:ok, direct} =
         CommonAPI.post(user_one, %{
@@ -544,6 +656,77 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
                }
              ] = result
     end
+
+    test "filtering", %{user: user, conn: conn} do
+      {:ok, list} = Pleroma.List.create("name", user)
+
+      local_user = insert(:user)
+      {:ok, local_activity} = CommonAPI.post(local_user, %{status: "Marisa is stupid."})
+      with_media = create_with_media_activity(local_user)
+      {:ok, list} = Pleroma.List.follow(list, local_user)
+
+      remote_user = insert(:user, local: false)
+      remote_activity = create_remote_activity(remote_user)
+      {:ok, list} = Pleroma.List.follow(list, remote_user)
+
+      all_ids =
+        conn
+        |> get("/api/v1/timelines/list/#{list.id}")
+        |> json_response_and_validate_schema(200)
+        |> Enum.map(& &1["id"])
+
+      assert local_activity.id in all_ids
+      assert with_media.id in all_ids
+      assert remote_activity.id in all_ids
+
+      only_local_ids =
+        conn
+        |> get("/api/v1/timelines/list/#{list.id}?local=true")
+        |> json_response_and_validate_schema(200)
+        |> Enum.map(& &1["id"])
+
+      assert local_activity.id in only_local_ids
+      assert with_media.id in only_local_ids
+      refute remote_activity.id in only_local_ids
+
+      only_local_media_ids =
+        conn
+        |> get("/api/v1/timelines/list/#{list.id}?local=true&only_media=true")
+        |> json_response_and_validate_schema(200)
+        |> Enum.map(& &1["id"])
+
+      refute local_activity.id in only_local_media_ids
+      assert with_media.id in only_local_media_ids
+      refute remote_activity.id in only_local_media_ids
+
+      remote_ids =
+        conn
+        |> get("/api/v1/timelines/list/#{list.id}?remote=true")
+        |> json_response_and_validate_schema(200)
+        |> Enum.map(& &1["id"])
+
+      refute local_activity.id in remote_ids
+      refute with_media.id in remote_ids
+      assert remote_activity.id in remote_ids
+
+      assert conn
+             |> get("/api/v1/timelines/list/#{list.id}?remote=true&only_media=true")
+             |> json_response_and_validate_schema(200) == []
+
+      only_media_ids =
+        conn
+        |> get("/api/v1/timelines/list/#{list.id}?only_media=true")
+        |> json_response_and_validate_schema(200)
+        |> Enum.map(& &1["id"])
+
+      refute local_activity.id in only_media_ids
+      assert with_media.id in only_media_ids
+      refute remote_activity.id in only_media_ids
+
+      assert conn
+             |> get("/api/v1/timelines/list/#{list.id}?only_media=true&local=true&remote=true")
+             |> json_response_and_validate_schema(200) == []
+    end
   end
 
   describe "hashtag" do
@@ -554,19 +737,85 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
       following = insert(:user)
 
       {:ok, activity} = CommonAPI.post(following, %{status: "test #2hu"})
+      with_media = create_with_media_activity(following)
 
-      nconn = get(conn, "/api/v1/timelines/tag/2hu")
+      remote = insert(:user, local: false)
+      remote_activity = create_remote_activity(remote)
 
-      assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok)
+      all_ids =
+        conn
+        |> get("/api/v1/timelines/tag/2hu")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
 
-      assert id == to_string(activity.id)
+      assert activity.id in all_ids
+      assert with_media.id in all_ids
+      assert remote_activity.id in all_ids
 
       # works for different capitalization too
-      nconn = get(conn, "/api/v1/timelines/tag/2HU")
+      all_ids =
+        conn
+        |> get("/api/v1/timelines/tag/2HU")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
+
+      assert activity.id in all_ids
+      assert with_media.id in all_ids
+      assert remote_activity.id in all_ids
+
+      local_ids =
+        conn
+        |> get("/api/v1/timelines/tag/2hu?local=true")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
 
-      assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok)
+      assert activity.id in local_ids
+      assert with_media.id in local_ids
+      refute remote_activity.id in local_ids
 
-      assert id == to_string(activity.id)
+      remote_ids =
+        conn
+        |> get("/api/v1/timelines/tag/2hu?remote=true")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
+
+      refute activity.id in remote_ids
+      refute with_media.id in remote_ids
+      assert remote_activity.id in remote_ids
+
+      media_ids =
+        conn
+        |> get("/api/v1/timelines/tag/2hu?only_media=true")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
+
+      refute activity.id in media_ids
+      assert with_media.id in media_ids
+      refute remote_activity.id in media_ids
+
+      media_local_ids =
+        conn
+        |> get("/api/v1/timelines/tag/2hu?only_media=true&local=true")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
+
+      refute activity.id in media_local_ids
+      assert with_media.id in media_local_ids
+      refute remote_activity.id in media_local_ids
+
+      ids =
+        conn
+        |> get("/api/v1/timelines/tag/2hu?only_media=true&local=true&remote=true")
+        |> json_response_and_validate_schema(:ok)
+        |> Enum.map(& &1["id"])
+
+      refute activity.id in ids
+      refute with_media.id in ids
+      refute remote_activity.id in ids
+
+      assert conn
+             |> get("/api/v1/timelines/tag/2hu?only_media=true&remote=true")
+             |> json_response_and_validate_schema(:ok) == []
     end
 
     test "multi-hashtag timeline", %{conn: conn} do
@@ -656,10 +905,10 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
       %{conn: auth_conn} = oauth_access(["read:statuses"])
 
       res_conn = get(auth_conn, "#{base_uri}?local=true")
-      assert length(json_response(res_conn, 200)) == 1
+      assert length(json_response_and_validate_schema(res_conn, 200)) == 1
 
       res_conn = get(auth_conn, "#{base_uri}?local=false")
-      assert length(json_response(res_conn, 200)) == 2
+      assert length(json_response_and_validate_schema(res_conn, 200)) == 2
     end
 
     test "with default settings on private instances, returns 403 for unauthenticated users", %{
@@ -673,7 +922,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
       for local <- [true, false] do
         res_conn = get(conn, "#{base_uri}?local=#{local}")
 
-        assert json_response(res_conn, :unauthorized) == error_response
+        assert json_response_and_validate_schema(res_conn, :unauthorized) == error_response
       end
 
       ensure_authenticated_access(base_uri)
@@ -690,7 +939,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
       for local <- [true, false] do
         res_conn = get(conn, "#{base_uri}?local=#{local}")
 
-        assert json_response(res_conn, :unauthorized) == error_response
+        assert json_response_and_validate_schema(res_conn, :unauthorized) == error_response
       end
 
       ensure_authenticated_access(base_uri)
@@ -702,10 +951,10 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
       clear_config([:restrict_unauthenticated, :timelines, :federated], true)
 
       res_conn = get(conn, "#{base_uri}?local=true")
-      assert length(json_response(res_conn, 200)) == 1
+      assert length(json_response_and_validate_schema(res_conn, 200)) == 1
 
       res_conn = get(conn, "#{base_uri}?local=false")
-      assert json_response(res_conn, :unauthorized) == error_response
+      assert json_response_and_validate_schema(res_conn, :unauthorized) == error_response
 
       ensure_authenticated_access(base_uri)
     end
@@ -717,13 +966,46 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
       clear_config([:restrict_unauthenticated, :timelines, :federated], false)
 
       res_conn = get(conn, "#{base_uri}?local=true")
-      assert json_response(res_conn, :unauthorized) == error_response
+      assert json_response_and_validate_schema(res_conn, :unauthorized) == error_response
 
       # Note: local activities get delivered as part of federated timeline
       res_conn = get(conn, "#{base_uri}?local=false")
-      assert length(json_response(res_conn, 200)) == 2
+      assert length(json_response_and_validate_schema(res_conn, 200)) == 2
 
       ensure_authenticated_access(base_uri)
     end
   end
+
+  defp create_remote_activity(user) do
+    obj =
+      insert(:note, %{
+        data: %{
+          "to" => [
+            "https://www.w3.org/ns/activitystreams#Public",
+            User.ap_followers(user)
+          ]
+        },
+        user: user
+      })
+
+    insert(:note_activity, %{
+      note: obj,
+      recipients: [
+        "https://www.w3.org/ns/activitystreams#Public",
+        User.ap_followers(user)
+      ],
+      user: user,
+      local: false
+    })
+  end
+
+  defp create_with_media_activity(user) do
+    obj = insert(:attachment_note, user: user)
+
+    insert(:note_activity, %{
+      note: obj,
+      recipients: ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(user)],
+      user: user
+    })
+  end
 end