diff options
| -rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex | 48 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api.ex | 10 | ||||
| -rw-r--r-- | lib/pleroma/web/pleroma_api/pleroma_api.ex | 40 | ||||
| -rw-r--r-- | lib/pleroma/web/pleroma_api/subscription_notification_controller.ex | 59 | ||||
| -rw-r--r-- | lib/pleroma/web/pleroma_api/views/subscription_notification_view.ex (renamed from lib/pleroma/web/mastodon_api/views/subscription_notification_view.ex) | 4 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 33 | ||||
| -rw-r--r-- | test/web/mastodon_api/mastodon_api_controller_test.exs | 192 | ||||
| -rw-r--r-- | test/web/pleroma_api/subscription_notification_controller_test.exs | 234 | 
8 files changed, 343 insertions, 277 deletions
| diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index eefdb8c06..060137b80 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -23,7 +23,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    alias Pleroma.Repo    alias Pleroma.ScheduledActivity    alias Pleroma.Stats -  alias Pleroma.SubscriptionNotification    alias Pleroma.User    alias Pleroma.Web    alias Pleroma.Web.ActivityPub.ActivityPub @@ -40,7 +39,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    alias Pleroma.Web.MastodonAPI.ReportView    alias Pleroma.Web.MastodonAPI.ScheduledActivityView    alias Pleroma.Web.MastodonAPI.StatusView -  alias Pleroma.Web.MastodonAPI.SubscriptionNotificationView    alias Pleroma.Web.MediaProxy    alias Pleroma.Web.OAuth.App    alias Pleroma.Web.OAuth.Authorization @@ -727,28 +725,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      |> render("index.json", %{notifications: notifications, for: user})    end -  def subscription_notifications(%{assigns: %{user: user}} = conn, params) do -    notifications = MastodonAPI.get_subscription_notifications(user, params) - -    conn -    |> add_link_headers(:subscription_notifications, notifications) -    |> put_view(SubscriptionNotificationView) -    |> render("index.json", %{notifications: notifications, for: user}) -  end - -  def get_subscription_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do -    with {:ok, notification} <- SubscriptionNotification.get(user, id) do -      conn -      |> put_view(SubscriptionNotificationView) -      |> render("show.json", %{subscription_notification: notification, for: user}) -    else -      {:error, reason} -> -        conn -        |> put_status(:forbidden) -        |> json(%{"error" => reason}) -    end -  end -    def get_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do      with {:ok, notification} <- Notification.get(user, id) do        conn @@ -767,11 +743,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      json(conn, %{})    end -  def clear_subscription_notifications(%{assigns: %{user: user}} = conn, _params) do -    SubscriptionNotification.clear(user) -    json(conn, %{}) -  end -    def dismiss_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do      with {:ok, _notif} <- Notification.dismiss(user, id) do        json(conn, %{}) @@ -783,30 +754,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      end    end -  def dismiss_subscription_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do -    with {:ok, _notif} <- SubscriptionNotification.dismiss(user, id) do -      json(conn, %{}) -    else -      {:error, reason} -> -        conn -        |> put_status(:forbidden) -        |> json(%{"error" => reason}) -    end -  end -    def destroy_multiple(%{assigns: %{user: user}} = conn, %{"ids" => ids} = _params) do      Notification.destroy_multiple(user, ids)      json(conn, %{})    end -  def destroy_multiple_subscription_notifications( -        %{assigns: %{user: user}} = conn, -        %{"ids" => ids} = _params -      ) do -    SubscriptionNotification.destroy_multiple(user, ids) -    json(conn, %{}) -  end -    def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do      id = List.wrap(id)      q = from(u in User, where: u.id in ^id) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 6751e24d8..ac01d1ff3 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -10,7 +10,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do    alias Pleroma.Notification    alias Pleroma.Pagination    alias Pleroma.ScheduledActivity -  alias Pleroma.SubscriptionNotification    alias Pleroma.User    alias Pleroma.Web.CommonAPI @@ -63,15 +62,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do      |> Pagination.fetch_paginated(params)    end -  def get_subscription_notifications(user, params \\ %{}) do -    options = cast_params(params) - -    user -    |> SubscriptionNotification.for_user_query(options) -    |> restrict(:exclude_types, options) -    |> Pagination.fetch_paginated(params) -  end -    def get_scheduled_activities(user, params \\ %{}) do      user      |> ScheduledActivity.for_user_query() diff --git a/lib/pleroma/web/pleroma_api/pleroma_api.ex b/lib/pleroma/web/pleroma_api/pleroma_api.ex new file mode 100644 index 000000000..480964845 --- /dev/null +++ b/lib/pleroma/web/pleroma_api/pleroma_api.ex @@ -0,0 +1,40 @@ +defmodule Pleroma.Web.PleromaAPI.PleromaAPI do +  import Ecto.Query +  import Ecto.Changeset + +  alias Pleroma.Activity +  alias Pleroma.Pagination +  alias Pleroma.SubscriptionNotification + +  def get_subscription_notifications(user, params \\ %{}) do +    options = cast_params(params) + +    user +    |> SubscriptionNotification.for_user_query(options) +    |> restrict(:exclude_types, options) +    |> Pagination.fetch_paginated(params) +  end + +  defp cast_params(params) do +    param_types = %{ +      exclude_types: {:array, :string}, +      reblogs: :boolean, +      with_muted: :boolean +    } + +    changeset = cast({%{}, param_types}, params, Map.keys(param_types)) +    changeset.changes +  end + +  defp restrict(query, :exclude_types, %{exclude_types: mastodon_types = [_ | _]}) do +    ap_types = +      mastodon_types +      |> Enum.map(&Activity.from_mastodon_notification_type/1) +      |> Enum.filter(& &1) + +    query +    |> where([q, a], not fragment("? @> ARRAY[?->>'type']::varchar[]", ^ap_types, a.data)) +  end + +  defp restrict(query, _, _), do: query +end diff --git a/lib/pleroma/web/pleroma_api/subscription_notification_controller.ex b/lib/pleroma/web/pleroma_api/subscription_notification_controller.ex new file mode 100644 index 000000000..bfc2631dd --- /dev/null +++ b/lib/pleroma/web/pleroma_api/subscription_notification_controller.ex @@ -0,0 +1,59 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.SubscriptionNotificationController do +  use Pleroma.Web, :controller + +  import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] + +  alias Pleroma.SubscriptionNotification +  alias Pleroma.Web.PleromaAPI.PleromaAPI +  alias Pleroma.Web.PleromaAPI.SubscriptionNotificationView + +  def list(%{assigns: %{user: user}} = conn, params) do +    notifications = PleromaAPI.get_subscription_notifications(user, params) + +    conn +    |> add_link_headers(notifications) +    |> put_view(SubscriptionNotificationView) +    |> render("index.json", %{notifications: notifications, for: user}) +  end + +  def get(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do +    with {:ok, notification} <- SubscriptionNotification.get(user, id) do +      conn +      |> put_view(SubscriptionNotificationView) +      |> render("show.json", %{subscription_notification: notification, for: user}) +    else +      {:error, reason} -> +        conn +        |> put_status(:forbidden) +        |> json(%{"error" => reason}) +    end +  end + +  def clear(%{assigns: %{user: user}} = conn, _params) do +    SubscriptionNotification.clear(user) +    json(conn, %{}) +  end + +  def dismiss(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do +    with {:ok, _notif} <- SubscriptionNotification.dismiss(user, id) do +      json(conn, %{}) +    else +      {:error, reason} -> +        conn +        |> put_status(:forbidden) +        |> json(%{"error" => reason}) +    end +  end + +  def destroy_multiple( +        %{assigns: %{user: user}} = conn, +        %{"ids" => ids} = _params +      ) do +    SubscriptionNotification.destroy_multiple(user, ids) +    json(conn, %{}) +  end +end diff --git a/lib/pleroma/web/mastodon_api/views/subscription_notification_view.ex b/lib/pleroma/web/pleroma_api/views/subscription_notification_view.ex index 83d2b647f..d7f7f4c5a 100644 --- a/lib/pleroma/web/mastodon_api/views/subscription_notification_view.ex +++ b/lib/pleroma/web/pleroma_api/views/subscription_notification_view.ex @@ -2,15 +2,15 @@  # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>  # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.MastodonAPI.SubscriptionNotificationView do +defmodule Pleroma.Web.PleromaAPI.SubscriptionNotificationView do    use Pleroma.Web, :view    alias Pleroma.Activity    alias Pleroma.User    alias Pleroma.Web.CommonAPI    alias Pleroma.Web.MastodonAPI.AccountView -  alias Pleroma.Web.MastodonAPI.SubscriptionNotificationView    alias Pleroma.Web.MastodonAPI.StatusView +  alias Pleroma.Web.PleromaAPI.SubscriptionNotificationView    def render("index.json", %{notifications: notifications, for: user}) do      safe_render_many(notifications, SubscriptionNotificationView, "show.json", %{for: user}) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 409fc9eca..05891b6c0 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -268,6 +268,14 @@ defmodule Pleroma.Web.Router do        pipe_through(:oauth_read)        get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)        get("/conversations/:id", PleromaAPIController, :conversation) + +      scope "/subscription_notifications" do +        post("/clear", SubscriptionNotificationController, :clear) +        post("/dismiss", SubscriptionNotificationController, :dismiss) +        delete("/destroy_multiple", SubscriptionNotificationController, :destroy_multiple) +        get("/", SubscriptionNotificationController, :list) +        get("/id", SubscriptionNotificationController, :get) +      end      end      scope [] do @@ -302,38 +310,13 @@ defmodule Pleroma.Web.Router do        post("/notifications/clear", MastodonAPIController, :clear_notifications) -      post( -        "/notifications/subscription/clear", -        MastodonAPIController, -        :clear_subscription_notifications -      ) -        post("/notifications/dismiss", MastodonAPIController, :dismiss_notification) -      post( -        "/notifications/subscription/dismiss", -        MastodonAPIController, -        :dismiss_subscription_notification -      ) -        get("/notifications", MastodonAPIController, :notifications) -      get("/notifications/subscription", MastodonAPIController, :subscription_notifications)        get("/notifications/:id", MastodonAPIController, :get_notification) -      get( -        "/notifications/subscription/:id", -        MastodonAPIController, -        :get_subscription_notification -      ) -        delete("/notifications/destroy_multiple", MastodonAPIController, :destroy_multiple) -      delete( -        "/notifications/subscription/destroy_multiple", -        MastodonAPIController, -        :destroy_multiple_subscription_notifications -      ) -        get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses)        get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status) diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 1d2d9e134..fb04748bb 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -13,7 +13,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do    alias Pleroma.Object    alias Pleroma.Repo    alias Pleroma.ScheduledActivity -  alias Pleroma.SubscriptionNotification    alias Pleroma.Tests.ObanHelpers    alias Pleroma.User    alias Pleroma.Web.ActivityPub.ActivityPub @@ -1275,197 +1274,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      end    end -  describe "subscription_notifications" do -    setup do -      user = insert(:user) -      subscriber = insert(:user) - -      User.subscribe(subscriber, user) - -      {:ok, %{user: user, subscriber: subscriber}} -    end - -    test "list of notifications", %{conn: conn, user: user, subscriber: subscriber} do -      status_text = "Hello" -      {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text}) - -      conn = -        conn -        |> assign(:user, subscriber) -        |> get("/api/v1/notifications/subscription") - -      assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200) -      assert response == status_text -    end - -    test "getting a single notification", %{conn: conn, user: user, subscriber: subscriber} do -      status_text = "Hello" - -      {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text}) -      [notification] = Repo.all(SubscriptionNotification) - -      conn = -        conn -        |> assign(:user, subscriber) -        |> get("/api/v1/notifications/subscription/#{notification.id}") - -      assert %{"status" => %{"content" => response}} = json_response(conn, 200) -      assert response == status_text -    end - -    test "dismissing a single notification also deletes it", %{ -      conn: conn, -      user: user, -      subscriber: subscriber -    } do -      status_text = "Hello" -      {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text}) - -      [notification] = Repo.all(SubscriptionNotification) - -      conn = -        conn -        |> assign(:user, subscriber) -        |> post("/api/v1/notifications/subscription/dismiss", %{"id" => notification.id}) - -      assert %{} = json_response(conn, 200) - -      assert Repo.all(SubscriptionNotification) == [] -    end - -    test "clearing all notifications also deletes them", %{ -      conn: conn, -      user: user, -      subscriber: subscriber -    } do -      status_text1 = "Hello" -      status_text2 = "Hello again" -      {:ok, _activity1} = CommonAPI.post(user, %{"status" => status_text1}) -      {:ok, _activity2} = CommonAPI.post(user, %{"status" => status_text2}) - -      conn = -        conn -        |> assign(:user, subscriber) -        |> post("/api/v1/notifications/subscription/clear") - -      assert %{} = json_response(conn, 200) - -      conn = -        build_conn() -        |> assign(:user, subscriber) -        |> get("/api/v1/notifications/subscription") - -      assert json_response(conn, 200) == [] - -      assert Repo.all(SubscriptionNotification) == [] -    end - -    test "paginates notifications using min_id, since_id, max_id, and limit", %{ -      conn: conn, -      user: user, -      subscriber: subscriber -    } do -      {:ok, activity1} = CommonAPI.post(user, %{"status" => "Hello 1"}) -      {:ok, activity2} = CommonAPI.post(user, %{"status" => "Hello 2"}) -      {:ok, activity3} = CommonAPI.post(user, %{"status" => "Hello 3"}) -      {:ok, activity4} = CommonAPI.post(user, %{"status" => "Hello 4"}) - -      notification1_id = -        Repo.get_by(SubscriptionNotification, activity_id: activity1.id).id |> to_string() - -      notification2_id = -        Repo.get_by(SubscriptionNotification, activity_id: activity2.id).id |> to_string() - -      notification3_id = -        Repo.get_by(SubscriptionNotification, activity_id: activity3.id).id |> to_string() - -      notification4_id = -        Repo.get_by(SubscriptionNotification, activity_id: activity4.id).id |> to_string() - -      conn = assign(conn, :user, subscriber) - -      # min_id -      conn_res = -        get(conn, "/api/v1/notifications/subscription?limit=2&min_id=#{notification1_id}") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result - -      # since_id -      conn_res = -        get(conn, "/api/v1/notifications/subscription?limit=2&since_id=#{notification1_id}") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result - -      # max_id -      conn_res = -        get(conn, "/api/v1/notifications/subscription?limit=2&max_id=#{notification4_id}") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result -    end - -    test "destroy multiple", %{conn: conn, user: user1, subscriber: user2} do -      # mutual subscription -      User.subscribe(user1, user2) - -      {:ok, activity1} = CommonAPI.post(user1, %{"status" => "Hello 1"}) -      {:ok, activity2} = CommonAPI.post(user1, %{"status" => "World 1"}) -      {:ok, activity3} = CommonAPI.post(user2, %{"status" => "Hello 2"}) -      {:ok, activity4} = CommonAPI.post(user2, %{"status" => "World 2"}) - -      notification1_id = -        Repo.get_by(SubscriptionNotification, activity_id: activity1.id).id |> to_string() - -      notification2_id = -        Repo.get_by(SubscriptionNotification, activity_id: activity2.id).id |> to_string() - -      notification3_id = -        Repo.get_by(SubscriptionNotification, activity_id: activity3.id).id |> to_string() - -      notification4_id = -        Repo.get_by(SubscriptionNotification, activity_id: activity4.id).id |> to_string() - -      conn = assign(conn, :user, user1) - -      conn_res = get(conn, "/api/v1/notifications/subscription") - -      result = json_response(conn_res, 200) - -      Enum.each(result, fn %{"id" => id} -> -        assert id in [notification3_id, notification4_id] -      end) - -      conn2 = assign(conn, :user, user2) - -      conn_res = get(conn2, "/api/v1/notifications/subscription") - -      result = json_response(conn_res, 200) - -      Enum.each(result, fn %{"id" => id} -> -        assert id in [notification1_id, notification2_id] -      end) - -      conn_destroy = -        delete(conn, "/api/v1/notifications/subscription/destroy_multiple", %{ -          "ids" => [notification3_id, notification4_id] -        }) - -      assert json_response(conn_destroy, 200) == %{} - -      conn_res = get(conn2, "/api/v1/notifications/subscription") - -      result = json_response(conn_res, 200) - -      Enum.each(result, fn %{"id" => id} -> -        assert id in [notification1_id, notification2_id] -      end) - -      assert length(Repo.all(SubscriptionNotification)) == 2 -    end -  end -    describe "reblogging" do      test "reblogs and returns the reblogged status", %{conn: conn} do        activity = insert(:note_activity) diff --git a/test/web/pleroma_api/subscription_notification_controller_test.exs b/test/web/pleroma_api/subscription_notification_controller_test.exs new file mode 100644 index 000000000..ee495f112 --- /dev/null +++ b/test/web/pleroma_api/subscription_notification_controller_test.exs @@ -0,0 +1,234 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.SubscriptionNotificationControllerTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.Repo +  alias Pleroma.SubscriptionNotification +  alias Pleroma.User +  alias Pleroma.Web.CommonAPI +  import Pleroma.Factory +  import Tesla.Mock + +  setup do +    mock(fn env -> apply(HttpRequestMock, :request, [env]) end) +    :ok +  end + +  clear_config([:instance, :public]) +  clear_config([:rich_media, :enabled]) + +  describe "subscription_notifications" do +    setup do +      user = insert(:user) +      subscriber = insert(:user) + +      User.subscribe(subscriber, user) + +      {:ok, %{user: user, subscriber: subscriber}} +    end + +    test "list of notifications", %{conn: conn, user: user, subscriber: subscriber} do +      status_text = "Hello" +      {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text}) +      path = subscription_notification_path(conn, :list) + +      conn = +        conn +        |> assign(:user, subscriber) +        |> get(path) + +      assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200) +      assert response == status_text +    end + +    test "getting a single notification", %{conn: conn, user: user, subscriber: subscriber} do +      status_text = "Hello" + +      {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text}) +      [notification] = Repo.all(SubscriptionNotification) + +      path = subscription_notification_path(conn, :get, id: notification.id) + +      conn = +        conn +        |> assign(:user, subscriber) +        |> get(path) + +      assert %{"status" => %{"content" => response}} = json_response(conn, 200) +      assert response == status_text +    end + +    test "dismissing a single notification also deletes it", %{ +      conn: conn, +      user: user, +      subscriber: subscriber +    } do +      status_text = "Hello" +      {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text}) + +      [notification] = Repo.all(SubscriptionNotification) + +      conn = +        conn +        |> assign(:user, subscriber) +        |> post(subscription_notification_path(conn, :dismiss), %{"id" => notification.id}) + +      assert %{} = json_response(conn, 200) + +      assert Repo.all(SubscriptionNotification) == [] +    end + +    test "clearing all notifications also deletes them", %{ +      conn: conn, +      user: user, +      subscriber: subscriber +    } do +      status_text1 = "Hello" +      status_text2 = "Hello again" +      {:ok, _activity1} = CommonAPI.post(user, %{"status" => status_text1}) +      {:ok, _activity2} = CommonAPI.post(user, %{"status" => status_text2}) + +      conn = +        conn +        |> assign(:user, subscriber) +        |> post(subscription_notification_path(conn, :clear)) + +      assert %{} = json_response(conn, 200) + +      conn = +        build_conn() +        |> assign(:user, subscriber) +        |> get(subscription_notification_path(conn, :list)) + +      assert json_response(conn, 200) == [] + +      assert Repo.all(SubscriptionNotification) == [] +    end + +    test "paginates notifications using min_id, since_id, max_id, and limit", %{ +      conn: conn, +      user: user, +      subscriber: subscriber +    } do +      {:ok, activity1} = CommonAPI.post(user, %{"status" => "Hello 1"}) +      {:ok, activity2} = CommonAPI.post(user, %{"status" => "Hello 2"}) +      {:ok, activity3} = CommonAPI.post(user, %{"status" => "Hello 3"}) +      {:ok, activity4} = CommonAPI.post(user, %{"status" => "Hello 4"}) + +      notification1_id = +        Repo.get_by(SubscriptionNotification, activity_id: activity1.id).id |> to_string() + +      notification2_id = +        Repo.get_by(SubscriptionNotification, activity_id: activity2.id).id |> to_string() + +      notification3_id = +        Repo.get_by(SubscriptionNotification, activity_id: activity3.id).id |> to_string() + +      notification4_id = +        Repo.get_by(SubscriptionNotification, activity_id: activity4.id).id |> to_string() + +      conn = assign(conn, :user, subscriber) + +      # min_id +      conn_res = +        get( +          conn, +          subscription_notification_path(conn, :list, %{ +            "limit" => 2, +            "min_id" => notification1_id +          }) +        ) + +      result = json_response(conn_res, 200) +      assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result + +      # since_id +      conn_res = +        get( +          conn, +          subscription_notification_path(conn, :list, %{ +            "limit" => 2, +            "since_id" => notification1_id +          }) +        ) + +      result = json_response(conn_res, 200) +      assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result + +      # max_id +      conn_res = +        get( +          conn, +          subscription_notification_path(conn, :list, %{ +            "limit" => 2, +            "max_id" => notification4_id +          }) +        ) + +      result = json_response(conn_res, 200) +      assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result +    end + +    test "destroy multiple", %{conn: conn, user: user1, subscriber: user2} do +      # mutual subscription +      User.subscribe(user1, user2) + +      {:ok, activity1} = CommonAPI.post(user1, %{"status" => "Hello 1"}) +      {:ok, activity2} = CommonAPI.post(user1, %{"status" => "World 1"}) +      {:ok, activity3} = CommonAPI.post(user2, %{"status" => "Hello 2"}) +      {:ok, activity4} = CommonAPI.post(user2, %{"status" => "World 2"}) + +      notification1_id = +        Repo.get_by(SubscriptionNotification, activity_id: activity1.id).id |> to_string() + +      notification2_id = +        Repo.get_by(SubscriptionNotification, activity_id: activity2.id).id |> to_string() + +      notification3_id = +        Repo.get_by(SubscriptionNotification, activity_id: activity3.id).id |> to_string() + +      notification4_id = +        Repo.get_by(SubscriptionNotification, activity_id: activity4.id).id |> to_string() + +      conn = assign(conn, :user, user1) + +      conn_res = get(conn, subscription_notification_path(conn, :list)) + +      result = json_response(conn_res, 200) + +      Enum.each(result, fn %{"id" => id} -> +        assert id in [notification3_id, notification4_id] +      end) + +      conn2 = assign(conn, :user, user2) + +      conn_res = get(conn2, subscription_notification_path(conn, :list)) + +      result = json_response(conn_res, 200) + +      Enum.each(result, fn %{"id" => id} -> +        assert id in [notification1_id, notification2_id] +      end) + +      conn_destroy = +        delete(conn, subscription_notification_path(conn, :destroy_multiple), %{ +          "ids" => [notification3_id, notification4_id] +        }) + +      assert json_response(conn_destroy, 200) == %{} + +      conn_res = get(conn2, subscription_notification_path(conn, :list)) + +      result = json_response(conn_res, 200) + +      Enum.each(result, fn %{"id" => id} -> +        assert id in [notification1_id, notification2_id] +      end) + +      assert length(Repo.all(SubscriptionNotification)) == 2 +    end +  end +end | 
