[#647] tests for web push
authorMaksim <parallel588@gmail.com>
Wed, 6 Mar 2019 13:20:12 +0000 (13:20 +0000)
committerkaniini <nenolod@gmail.com>
Wed, 6 Mar 2019 13:20:12 +0000 (13:20 +0000)
14 files changed:
config/test.exs
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
lib/pleroma/web/mastodon_api/subscription_controller.ex [new file with mode: 0644]
lib/pleroma/web/mastodon_api/views/push_subscription_view.ex
lib/pleroma/web/push/impl.ex [new file with mode: 0644]
lib/pleroma/web/push/push.ex
lib/pleroma/web/push/subscription.ex
lib/pleroma/web/router.ex
test/support/factory.ex
test/support/web_push_http_client_mock.ex [new file with mode: 0644]
test/web/mastodon_api/push_subscription_view_test.exs [new file with mode: 0644]
test/web/mastodon_api/subscription_controller_test.exs [new file with mode: 0644]
test/web/push/impl_test.exs [new file with mode: 0644]
test/web/push/push_test.exs [deleted file]

index fbeba0919e89da5164231c012a9825b9d8bb0911..6dfa698c834e86a35b37c5df899e69c1ba129f08 100644 (file)
@@ -44,6 +44,8 @@ config :web_push_encryption, :vapid_details,
     "BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
   private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
 
+config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock
+
 config :pleroma, Pleroma.Jobs, testing: [max_jobs: 2]
 
 try do
index 4cd27c7a0af4ef5561ae8114e1ebc62be019ac77..4fe66f8567acc85795a6cff6355d41b2c589036e 100644 (file)
@@ -15,14 +15,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
   alias Pleroma.Web
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.MediaProxy
-  alias Pleroma.Web.Push
-  alias Push.Subscription
 
   alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.MastodonAPI.FilterView
   alias Pleroma.Web.MastodonAPI.ListView
   alias Pleroma.Web.MastodonAPI.MastodonView
-  alias Pleroma.Web.MastodonAPI.PushSubscriptionView
   alias Pleroma.Web.MastodonAPI.StatusView
   alias Pleroma.Web.MastodonAPI.ReportView
   alias Pleroma.Web.ActivityPub.ActivityPub
@@ -300,7 +297,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       |> Map.put(:visibility, "direct")
 
     activities =
-      ActivityPub.fetch_activities_query([user.ap_id], params)
+      [user.ap_id]
+      |> ActivityPub.fetch_activities_query(params)
       |> Repo.all()
 
     conn
@@ -1419,37 +1417,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     json(conn, %{})
   end
 
-  def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do
-    true = Push.enabled()
-    Subscription.delete_if_exists(user, token)
-    {:ok, subscription} = Subscription.create(user, token, params)
-    view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
-    json(conn, view)
-  end
-
-  def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
-    true = Push.enabled()
-    subscription = Subscription.get(user, token)
-    view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
-    json(conn, view)
-  end
-
-  def update_push_subscription(
-        %{assigns: %{user: user, token: token}} = conn,
-        params
-      ) do
-    true = Push.enabled()
-    {:ok, subscription} = Subscription.update(user, token, params)
-    view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
-    json(conn, view)
-  end
-
-  def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
-    true = Push.enabled()
-    {:ok, _response} = Subscription.delete(user, token)
-    json(conn, %{})
-  end
-
+  # fallback action
+  #
   def errors(conn, _) do
     conn
     |> put_status(500)
diff --git a/lib/pleroma/web/mastodon_api/subscription_controller.ex b/lib/pleroma/web/mastodon_api/subscription_controller.ex
new file mode 100644 (file)
index 0000000..b6c8ff8
--- /dev/null
@@ -0,0 +1,71 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
+  @moduledoc "The module represents functions to manage user subscriptions."
+  use Pleroma.Web, :controller
+
+  alias Pleroma.Web.Push
+  alias Pleroma.Web.Push.Subscription
+  alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
+
+  action_fallback(:errors)
+
+  # Creates PushSubscription
+  # POST /api/v1/push/subscription
+  #
+  def create(%{assigns: %{user: user, token: token}} = conn, params) do
+    with true <- Push.enabled(),
+         {:ok, _} <- Subscription.delete_if_exists(user, token),
+         {:ok, subscription} <- Subscription.create(user, token, params) do
+      view = View.render("push_subscription.json", subscription: subscription)
+      json(conn, view)
+    end
+  end
+
+  # Gets PushSubscription
+  # GET /api/v1/push/subscription
+  #
+  def get(%{assigns: %{user: user, token: token}} = conn, _params) do
+    with true <- Push.enabled(),
+         {:ok, subscription} <- Subscription.get(user, token) do
+      view = View.render("push_subscription.json", subscription: subscription)
+      json(conn, view)
+    end
+  end
+
+  # Updates PushSubscription
+  # PUT /api/v1/push/subscription
+  #
+  def update(%{assigns: %{user: user, token: token}} = conn, params) do
+    with true <- Push.enabled(),
+         {:ok, subscription} <- Subscription.update(user, token, params) do
+      view = View.render("push_subscription.json", subscription: subscription)
+      json(conn, view)
+    end
+  end
+
+  # Deletes PushSubscription
+  # DELETE /api/v1/push/subscription
+  #
+  def delete(%{assigns: %{user: user, token: token}} = conn, _params) do
+    with true <- Push.enabled(),
+         {:ok, _response} <- Subscription.delete(user, token),
+         do: json(conn, %{})
+  end
+
+  # fallback action
+  #
+  def errors(conn, {:error, :not_found}) do
+    conn
+    |> put_status(404)
+    |> json("Not found")
+  end
+
+  def errors(conn, _) do
+    conn
+    |> put_status(500)
+    |> json("Something went wrong")
+  end
+end
index e86b789c53abb395fab8cefcfe3478e734519f31..021489711c033d0995c85f76e37721fa0938d257 100644 (file)
@@ -4,6 +4,7 @@
 
 defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
   use Pleroma.Web, :view
+  alias Pleroma.Web.Push
 
   def render("push_subscription.json", %{subscription: subscription}) do
     %{
@@ -14,7 +15,5 @@ defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
     }
   end
 
-  defp server_key do
-    Keyword.get(Application.get_env(:web_push_encryption, :vapid_details), :public_key)
-  end
+  defp server_key, do: Keyword.get(Push.vapid_config(), :public_key)
 end
diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex
new file mode 100644 (file)
index 0000000..33f912d
--- /dev/null
@@ -0,0 +1,127 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Push.Impl do
+  @moduledoc "The module represents implementation push web notification"
+
+  alias Pleroma.Repo
+  alias Pleroma.User
+  alias Pleroma.Activity
+  alias Pleroma.Object
+  alias Pleroma.Web.Push.Subscription
+  alias Pleroma.Web.Metadata.Utils
+  alias Pleroma.Notification
+
+  require Logger
+  import Ecto.Query
+
+  @types ["Create", "Follow", "Announce", "Like"]
+
+  @doc "Performs sending notifications for user subscriptions"
+  @spec perform_send(Notification.t()) :: list(any)
+  def perform_send(%{activity: %{data: %{"type" => activity_type}}, user_id: user_id} = notif)
+      when activity_type in @types do
+    actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
+
+    type = Activity.mastodon_notification_type(notif.activity)
+    gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
+    avatar_url = User.avatar_url(actor)
+
+    for subscription <- fetch_subsriptions(user_id),
+        get_in(subscription.data, ["alerts", type]) do
+      %{
+        title: format_title(notif),
+        access_token: subscription.token.token,
+        body: format_body(notif, actor),
+        notification_id: notif.id,
+        notification_type: type,
+        icon: avatar_url,
+        preferred_locale: "en"
+      }
+      |> Jason.encode!()
+      |> push_message(build_sub(subscription), gcm_api_key, subscription)
+    end
+  end
+
+  def perform_send(_) do
+    Logger.warn("Unknown notification type")
+    :error
+  end
+
+  @doc "Push message to web"
+  def push_message(body, sub, api_key, subscription) do
+    case WebPushEncryption.send_web_push(body, sub, api_key) do
+      {:ok, %{status_code: code}} when 400 <= code and code < 500 ->
+        Logger.debug("Removing subscription record")
+        Repo.delete!(subscription)
+        :ok
+
+      {:ok, %{status_code: code}} when 200 <= code and code < 300 ->
+        :ok
+
+      {:ok, %{status_code: code}} ->
+        Logger.error("Web Push Notification failed with code: #{code}")
+        :error
+
+      _ ->
+        Logger.error("Web Push Notification failed with unknown error")
+        :error
+    end
+  end
+
+  @doc "Gets user subscriptions"
+  def fetch_subsriptions(user_id) do
+    Subscription
+    |> where(user_id: ^user_id)
+    |> preload(:token)
+    |> Repo.all()
+  end
+
+  def build_sub(subscription) do
+    %{
+      keys: %{
+        p256dh: subscription.key_p256dh,
+        auth: subscription.key_auth
+      },
+      endpoint: subscription.endpoint
+    }
+  end
+
+  def format_body(
+        %{activity: %{data: %{"type" => "Create", "object" => %{"content" => content}}}},
+        actor
+      ) do
+    "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
+  end
+
+  def format_body(
+        %{activity: %{data: %{"type" => "Announce", "object" => activity_id}}},
+        actor
+      ) do
+    %Activity{data: %{"object" => %{"id" => object_id}}} = Activity.get_by_ap_id(activity_id)
+    %Object{data: %{"content" => content}} = Object.get_by_ap_id(object_id)
+
+    "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
+  end
+
+  def format_body(
+        %{activity: %{data: %{"type" => type}}},
+        actor
+      )
+      when type in ["Follow", "Like"] do
+    case type do
+      "Follow" -> "@#{actor.nickname} has followed you"
+      "Like" -> "@#{actor.nickname} has favorited your post"
+    end
+  end
+
+  def format_title(%{activity: %{data: %{"type" => type}}}) do
+    case type do
+      "Create" -> "New Mention"
+      "Follow" -> "New Follower"
+      "Announce" -> "New Repeat"
+      "Like" -> "New Favorite"
+    end
+  end
+end
index 92f8f9ab461b05472fd8f1337a4e62ff600a771b..951dab53531fd6f13c28bac7cd6eb5fdf381b853 100644 (file)
@@ -5,17 +5,13 @@
 defmodule Pleroma.Web.Push do
   use GenServer
 
-  alias Pleroma.Repo
-  alias Pleroma.User
-  alias Pleroma.Activity
-  alias Pleroma.Object
-  alias Pleroma.Web.Push.Subscription
-  alias Pleroma.Web.Metadata.Utils
+  alias Pleroma.Web.Push.Impl
 
   require Logger
-  import Ecto.Query
 
-  @types ["Create", "Follow", "Announce", "Like"]
+  ##############
+  # Client API #
+  ##############
 
   def start_link() do
     GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
@@ -33,14 +29,18 @@ defmodule Pleroma.Web.Push do
     end
   end
 
-  def send(notification) do
-    if enabled() do
-      GenServer.cast(Pleroma.Web.Push, {:send, notification})
-    end
-  end
+  def send(notification),
+    do: GenServer.cast(__MODULE__, {:send, notification})
+
+  ####################
+  # Server Callbacks #
+  ####################
 
+  @impl true
   def init(:ok) do
-    if !enabled() do
+    if enabled() do
+      {:ok, nil}
+    else
       Logger.warn("""
       VAPID key pair is not found. If you wish to enabled web push, please run
 
@@ -50,112 +50,15 @@ defmodule Pleroma.Web.Push do
       """)
 
       :ignore
-    else
-      {:ok, nil}
     end
   end
 
-  def handle_cast(
-        {:send, %{activity: %{data: %{"type" => type}}, user_id: user_id} = notification},
-        state
-      )
-      when type in @types do
-    actor = User.get_cached_by_ap_id(notification.activity.data["actor"])
-
-    type = Pleroma.Activity.mastodon_notification_type(notification.activity)
-
-    Subscription
-    |> where(user_id: ^user_id)
-    |> preload(:token)
-    |> Repo.all()
-    |> Enum.filter(fn subscription ->
-      get_in(subscription.data, ["alerts", type]) || false
-    end)
-    |> Enum.each(fn subscription ->
-      sub = %{
-        keys: %{
-          p256dh: subscription.key_p256dh,
-          auth: subscription.key_auth
-        },
-        endpoint: subscription.endpoint
-      }
-
-      body =
-        Jason.encode!(%{
-          title: format_title(notification),
-          access_token: subscription.token.token,
-          body: format_body(notification, actor),
-          notification_id: notification.id,
-          notification_type: type,
-          icon: User.avatar_url(actor),
-          preferred_locale: "en"
-        })
-
-      case WebPushEncryption.send_web_push(
-             body,
-             sub,
-             Application.get_env(:web_push_encryption, :gcm_api_key)
-           ) do
-        {:ok, %{status_code: code}} when 400 <= code and code < 500 ->
-          Logger.debug("Removing subscription record")
-          Repo.delete!(subscription)
-          :ok
-
-        {:ok, %{status_code: code}} when 200 <= code and code < 300 ->
-          :ok
-
-        {:ok, %{status_code: code}} ->
-          Logger.error("Web Push Notification failed with code: #{code}")
-          :error
-
-        _ ->
-          Logger.error("Web Push Notification failed with unknown error")
-          :error
-      end
-    end)
-
-    {:noreply, state}
-  end
-
-  def handle_cast({:send, _}, state) do
-    Logger.warn("Unknown notification type")
-    {:noreply, state}
-  end
-
-  def format_body(
-        %{activity: %{data: %{"type" => "Create", "object" => %{"content" => content}}}},
-        actor
-      ) do
-    "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
-  end
-
-  def format_body(
-        %{activity: %{data: %{"type" => "Announce", "object" => activity_id}}},
-        actor
-      ) do
-    %Activity{data: %{"object" => %{"id" => object_id}}} = Activity.get_by_ap_id(activity_id)
-    %Object{data: %{"content" => content}} = Object.get_by_ap_id(object_id)
-
-    "@#{actor.nickname} repeated: #{Utils.scrub_html_and_truncate(content, 80)}"
-  end
-
-  def format_body(
-        %{activity: %{data: %{"type" => type}}},
-        actor
-      )
-      when type in ["Follow", "Like"] do
-    case type do
-      "Follow" -> "@#{actor.nickname} has followed you"
-      "Like" -> "@#{actor.nickname} has favorited your post"
+  @impl true
+  def handle_cast({:send, notification}, state) do
+    if enabled() do
+      Impl.perform_send(notification)
     end
-  end
 
-  defp format_title(%{activity: %{data: %{"type" => type}}}) do
-    case type do
-      "Create" -> "New Mention"
-      "Follow" -> "New Follower"
-      "Announce" -> "New Repeat"
-      "Like" -> "New Favorite"
-    end
+    {:noreply, state}
   end
 end
index 242e30910747cf32576b70080077ee1d24448cb4..c90bd2bda005e849f7740a79aa68f280175c5b49 100644 (file)
@@ -12,6 +12,8 @@ defmodule Pleroma.Web.Push.Subscription do
   alias Pleroma.Web.OAuth.Token
   alias Pleroma.Web.Push.Subscription
 
+  @type t :: %__MODULE__{}
+
   schema "push_subscriptions" do
     belongs_to(:user, User, type: Pleroma.FlakeId)
     belongs_to(:token, Token)
@@ -50,24 +52,32 @@ defmodule Pleroma.Web.Push.Subscription do
     })
   end
 
+  @doc "Gets subsciption by user & token"
+  @spec get(User.t(), Token.t()) :: {:ok, t()} | {:error, :not_found}
   def get(%User{id: user_id}, %Token{id: token_id}) do
-    Repo.get_by(Subscription, user_id: user_id, token_id: token_id)
+    case Repo.get_by(Subscription, user_id: user_id, token_id: token_id) do
+      nil -> {:error, :not_found}
+      subscription -> {:ok, subscription}
+    end
   end
 
   def update(user, token, params) do
-    get(user, token)
-    |> change(data: alerts(params))
-    |> Repo.update()
+    with {:ok, subscription} <- get(user, token) do
+      subscription
+      |> change(data: alerts(params))
+      |> Repo.update()
+    end
   end
 
   def delete(user, token) do
-    Repo.delete(get(user, token))
+    with {:ok, subscription} <- get(user, token),
+         do: Repo.delete(subscription)
   end
 
   def delete_if_exists(user, token) do
     case get(user, token) do
-      nil -> {:ok, nil}
-      sub -> Repo.delete(sub)
+      {:error, _} -> {:ok, nil}
+      {:ok, sub} -> Repo.delete(sub)
     end
   end
 
index 6fcb46878c56fe217ca41da52244fbcc93e226ad..fc322756a59d7daac04dc413520f2817e36bf3d3 100644 (file)
@@ -304,10 +304,10 @@ defmodule Pleroma.Web.Router do
     scope [] do
       pipe_through(:oauth_push)
 
-      post("/push/subscription", MastodonAPIController, :create_push_subscription)
-      get("/push/subscription", MastodonAPIController, :get_push_subscription)
-      put("/push/subscription", MastodonAPIController, :update_push_subscription)
-      delete("/push/subscription", MastodonAPIController, :delete_push_subscription)
+      post("/push/subscription", SubscriptionController, :create)
+      get("/push/subscription", SubscriptionController, :get)
+      put("/push/subscription", SubscriptionController, :update)
+      delete("/push/subscription", SubscriptionController, :delete)
     end
   end
 
index c025aaf21326a105d9e6ef5bb55caad0215ad21b..18f77f01a836b644debda3df8dafea9fcb790d01 100644 (file)
@@ -229,15 +229,32 @@ defmodule Pleroma.Factory do
   end
 
   def oauth_token_factory do
-    user = insert(:user)
     oauth_app = insert(:oauth_app)
 
     %Pleroma.Web.OAuth.Token{
       token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(),
       refresh_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(),
-      user_id: user.id,
+      user: build(:user),
       app_id: oauth_app.id,
       valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10)
     }
   end
+
+  def push_subscription_factory do
+    %Pleroma.Web.Push.Subscription{
+      user: build(:user),
+      token: build(:oauth_token),
+      endpoint: "https://example.com/example/1234",
+      key_auth: "8eDyX_uCN0XRhSbY5hs7Hg==",
+      key_p256dh:
+        "BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA=",
+      data: %{}
+    }
+  end
+
+  def notification_factory do
+    %Pleroma.Notification{
+      user: build(:user)
+    }
+  end
 end
diff --git a/test/support/web_push_http_client_mock.ex b/test/support/web_push_http_client_mock.ex
new file mode 100644 (file)
index 0000000..d8accd2
--- /dev/null
@@ -0,0 +1,23 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.WebPushHttpClientMock do
+  def get(url, headers \\ [], options \\ []) do
+    {
+      res,
+      %Tesla.Env{status: status}
+    } = Pleroma.HTTP.request(:get, url, "", headers, options)
+
+    {res, %{status_code: status}}
+  end
+
+  def post(url, body, headers \\ [], options \\ []) do
+    {
+      res,
+      %Tesla.Env{status: status}
+    } = Pleroma.HTTP.request(:post, url, body, headers, options)
+
+    {res, %{status_code: status}}
+  end
+end
diff --git a/test/web/mastodon_api/push_subscription_view_test.exs b/test/web/mastodon_api/push_subscription_view_test.exs
new file mode 100644 (file)
index 0000000..dc935fc
--- /dev/null
@@ -0,0 +1,23 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.PushSubscriptionViewTest do
+  use Pleroma.DataCase
+  import Pleroma.Factory
+  alias Pleroma.Web.MastodonAPI.PushSubscriptionView, as: View
+  alias Pleroma.Web.Push
+
+  test "Represent a subscription" do
+    subscription = insert(:push_subscription, data: %{"alerts" => %{"mention" => true}})
+
+    expected = %{
+      alerts: %{"mention" => true},
+      endpoint: subscription.endpoint,
+      id: to_string(subscription.id),
+      server_key: Keyword.get(Push.vapid_config(), :public_key)
+    }
+
+    assert expected == View.render("push_subscription.json", %{subscription: subscription})
+  end
+end
diff --git a/test/web/mastodon_api/subscription_controller_test.exs b/test/web/mastodon_api/subscription_controller_test.exs
new file mode 100644 (file)
index 0000000..7dfb02f
--- /dev/null
@@ -0,0 +1,192 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do
+  use Pleroma.Web.ConnCase
+
+  import Pleroma.Factory
+  alias Pleroma.Web.Push
+  alias Pleroma.Web.Push.Subscription
+
+  @sub %{
+    "endpoint" => "https://example.com/example/1234",
+    "keys" => %{
+      "auth" => "8eDyX_uCN0XRhSbY5hs7Hg==",
+      "p256dh" =>
+        "BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA="
+    }
+  }
+  @server_key Keyword.get(Push.vapid_config(), :public_key)
+
+  setup do
+    user = insert(:user)
+    token = insert(:oauth_token, user: user, scopes: ["push"])
+
+    conn =
+      build_conn()
+      |> assign(:user, user)
+      |> assign(:token, token)
+
+    %{conn: conn, user: user, token: token}
+  end
+
+  defmacro assert_error_when_disable_push(do: yield) do
+    quote do
+      vapid_details = Application.get_env(:web_push_encryption, :vapid_details, [])
+      Application.put_env(:web_push_encryption, :vapid_details, [])
+      assert "Something went wrong" == unquote(yield)
+      Application.put_env(:web_push_encryption, :vapid_details, vapid_details)
+    end
+  end
+
+  describe "creates push subscription" do
+    test "returns error when push disabled ", %{conn: conn} do
+      assert_error_when_disable_push do
+        conn
+        |> post("/api/v1/push/subscription", %{})
+        |> json_response(500)
+      end
+    end
+
+    test "successful creation", %{conn: conn} do
+      result =
+        conn
+        |> post("/api/v1/push/subscription", %{
+          "data" => %{"alerts" => %{"mention" => true, "test" => true}},
+          "subscription" => @sub
+        })
+        |> json_response(200)
+
+      [subscription] = Pleroma.Repo.all(Subscription)
+
+      assert %{
+               "alerts" => %{"mention" => true},
+               "endpoint" => subscription.endpoint,
+               "id" => to_string(subscription.id),
+               "server_key" => @server_key
+             } == result
+    end
+  end
+
+  describe "gets a user subscription" do
+    test "returns error when push disabled ", %{conn: conn} do
+      assert_error_when_disable_push do
+        conn
+        |> get("/api/v1/push/subscription", %{})
+        |> json_response(500)
+      end
+    end
+
+    test "returns error when user hasn't subscription", %{conn: conn} do
+      res =
+        conn
+        |> get("/api/v1/push/subscription", %{})
+        |> json_response(404)
+
+      assert "Not found" == res
+    end
+
+    test "returns a user subsciption", %{conn: conn, user: user, token: token} do
+      subscription =
+        insert(:push_subscription,
+          user: user,
+          token: token,
+          data: %{"alerts" => %{"mention" => true}}
+        )
+
+      res =
+        conn
+        |> get("/api/v1/push/subscription", %{})
+        |> json_response(200)
+
+      expect = %{
+        "alerts" => %{"mention" => true},
+        "endpoint" => "https://example.com/example/1234",
+        "id" => to_string(subscription.id),
+        "server_key" => @server_key
+      }
+
+      assert expect == res
+    end
+  end
+
+  describe "updates a user subsciption" do
+    setup %{conn: conn, user: user, token: token} do
+      subscription =
+        insert(:push_subscription,
+          user: user,
+          token: token,
+          data: %{"alerts" => %{"mention" => true}}
+        )
+
+      %{conn: conn, user: user, token: token, subscription: subscription}
+    end
+
+    test "returns error when push disabled ", %{conn: conn} do
+      assert_error_when_disable_push do
+        conn
+        |> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}})
+        |> json_response(500)
+      end
+    end
+
+    test "returns updated subsciption", %{conn: conn, subscription: subscription} do
+      res =
+        conn
+        |> put("/api/v1/push/subscription", %{
+          data: %{"alerts" => %{"mention" => false, "follow" => true}}
+        })
+        |> json_response(200)
+
+      expect = %{
+        "alerts" => %{"follow" => true, "mention" => false},
+        "endpoint" => "https://example.com/example/1234",
+        "id" => to_string(subscription.id),
+        "server_key" => @server_key
+      }
+
+      assert expect == res
+    end
+  end
+
+  describe "deletes the user subscription" do
+    test "returns error when push disabled ", %{conn: conn} do
+      assert_error_when_disable_push do
+        conn
+        |> delete("/api/v1/push/subscription", %{})
+        |> json_response(500)
+      end
+    end
+
+    test "returns error when user hasn't subscription", %{conn: conn} do
+      res =
+        conn
+        |> delete("/api/v1/push/subscription", %{})
+        |> json_response(404)
+
+      assert "Not found" == res
+    end
+
+    test "returns empty result and delete user subsciption", %{
+      conn: conn,
+      user: user,
+      token: token
+    } do
+      subscription =
+        insert(:push_subscription,
+          user: user,
+          token: token,
+          data: %{"alerts" => %{"mention" => true}}
+        )
+
+      res =
+        conn
+        |> delete("/api/v1/push/subscription", %{})
+        |> json_response(200)
+
+      assert %{} == res
+      refute Pleroma.Repo.get(Subscription, subscription.id)
+    end
+  end
+end
diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs
new file mode 100644 (file)
index 0000000..3f9f3d8
--- /dev/null
@@ -0,0 +1,145 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Push.ImplTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Web.Push.Impl
+  alias Pleroma.Web.Push.Subscription
+
+  import Pleroma.Factory
+
+  setup_all do
+    Tesla.Mock.mock_global(fn
+      %{method: :post, url: "https://example.com/example/1234"} ->
+        %Tesla.Env{status: 200}
+
+      %{method: :post, url: "https://example.com/example/not_found"} ->
+        %Tesla.Env{status: 400}
+
+      %{method: :post, url: "https://example.com/example/bad"} ->
+        %Tesla.Env{status: 100}
+    end)
+
+    :ok
+  end
+
+  @sub %{
+    endpoint: "https://example.com/example/1234",
+    keys: %{
+      auth: "8eDyX_uCN0XRhSbY5hs7Hg==",
+      p256dh:
+        "BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA="
+    }
+  }
+  @api_key "BASgACIHpN1GYgzSRp"
+  @message "@Bob: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..."
+
+  test "performs sending notifications" do
+    user = insert(:user)
+    user2 = insert(:user)
+    insert(:push_subscription, user: user, data: %{alerts: %{"mention" => true}})
+    insert(:push_subscription, user: user2, data: %{alerts: %{"mention" => true}})
+
+    insert(:push_subscription,
+      user: user,
+      data: %{alerts: %{"follow" => true, "mention" => true}}
+    )
+
+    insert(:push_subscription,
+      user: user,
+      data: %{alerts: %{"follow" => true, "mention" => false}}
+    )
+
+    notif =
+      insert(:notification,
+        user: user,
+        activity: %Pleroma.Activity{
+          data: %{
+            "type" => "Create",
+            "actor" => user.ap_id,
+            "object" => %{"content" => "<Lorem ipsum dolor sit amet."}
+          }
+        }
+      )
+
+    assert Impl.perform_send(notif) == [:ok, :ok]
+  end
+
+  test "returns error if notif does not match " do
+    assert Impl.perform_send(%{}) == :error
+  end
+
+  test "successful message sending" do
+    assert Impl.push_message(@message, @sub, @api_key, %Subscription{}) == :ok
+  end
+
+  test "fail message sending" do
+    assert Impl.push_message(
+             @message,
+             Map.merge(@sub, %{endpoint: "https://example.com/example/bad"}),
+             @api_key,
+             %Subscription{}
+           ) == :error
+  end
+
+  test "delete subsciption if restult send message between 400..500" do
+    subscription = insert(:push_subscription)
+
+    assert Impl.push_message(
+             @message,
+             Map.merge(@sub, %{endpoint: "https://example.com/example/not_found"}),
+             @api_key,
+             subscription
+           ) == :ok
+
+    refute Pleroma.Repo.get(Subscription, subscription.id)
+  end
+
+  test "renders body for create activity" do
+    assert Impl.format_body(
+             %{
+               activity: %{
+                 data: %{
+                   "type" => "Create",
+                   "object" => %{
+                     "content" =>
+                       "<span>Lorem ipsum dolor sit amet</span>, consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis."
+                   }
+                 }
+               }
+             },
+             %{nickname: "Bob"}
+           ) ==
+             "@Bob: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..."
+  end
+
+  test "renders body for follow activity" do
+    assert Impl.format_body(%{activity: %{data: %{"type" => "Follow"}}}, %{nickname: "Bob"}) ==
+             "@Bob has followed you"
+  end
+
+  test "renders body for announce activity" do
+    user = insert(:user)
+
+    note =
+      insert(:note, %{
+        data: %{
+          "content" =>
+            "<span>Lorem ipsum dolor sit amet</span>, consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis."
+        }
+      })
+
+    note_activity = insert(:note_activity, %{note: note})
+    announce_activity = insert(:announce_activity, %{user: user, note_activity: note_activity})
+
+    assert Impl.format_body(%{activity: announce_activity}, user) ==
+             "@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..."
+  end
+
+  test "renders body for like activity" do
+    assert Impl.format_body(%{activity: %{data: %{"type" => "Like"}}}, %{nickname: "Bob"}) ==
+             "@Bob has favorited your post"
+  end
+end
diff --git a/test/web/push/push_test.exs b/test/web/push/push_test.exs
deleted file mode 100644 (file)
index 5fa9753..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-defmodule Pleroma.Web.PushTest do
-  use Pleroma.DataCase
-
-  alias Pleroma.Web.Push
-
-  import Pleroma.Factory
-
-  test "renders body for create activity" do
-    assert Push.format_body(
-             %{
-               activity: %{
-                 data: %{
-                   "type" => "Create",
-                   "object" => %{
-                     "content" =>
-                       "<span>Lorem ipsum dolor sit amet</span>, consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis."
-                   }
-                 }
-               }
-             },
-             %{nickname: "Bob"}
-           ) ==
-             "@Bob: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..."
-  end
-
-  test "renders body for follow activity" do
-    assert Push.format_body(%{activity: %{data: %{"type" => "Follow"}}}, %{nickname: "Bob"}) ==
-             "@Bob has followed you"
-  end
-
-  test "renders body for announce activity" do
-    user = insert(:user)
-
-    note =
-      insert(:note, %{
-        data: %{
-          "content" =>
-            "<span>Lorem ipsum dolor sit amet</span>, consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis."
-        }
-      })
-
-    note_activity = insert(:note_activity, %{note: note})
-    announce_activity = insert(:announce_activity, %{user: user, note_activity: note_activity})
-
-    assert Push.format_body(%{activity: announce_activity}, user) ==
-             "@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur  adipiscing elit. Fusce sagittis fini..."
-  end
-
-  test "renders body for like activity" do
-    assert Push.format_body(%{activity: %{data: %{"type" => "Like"}}}, %{nickname: "Bob"}) ==
-             "@Bob has favorited your post"
-  end
-end