[#534] Various tweaks. Tests for Instances and Instance.
authorIvan Tashkinov <ivantashkinov@gmail.com>
Mon, 28 Jan 2019 12:25:06 +0000 (15:25 +0300)
committerIvan Tashkinov <ivantashkinov@gmail.com>
Mon, 28 Jan 2019 12:25:06 +0000 (15:25 +0300)
15 files changed:
config/config.exs
lib/pleroma/instances.ex
lib/pleroma/instances/instance.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/salmon/salmon.ex
lib/pleroma/web/websub/websub.ex
test/support/factory.ex
test/support/http_request_mock.ex
test/web/activity_pub/activity_pub_controller_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/federator_test.exs
test/web/instances/instance_test.exs [new file with mode: 0644]
test/web/instances/instances_test.exs [new file with mode: 0644]
test/web/ostatus/ostatus_controller_test.exs
test/web/websub/websub_controller_test.exs

index 7a1a875c981c46ec2754ac865721160eb68faa27..c0a936b17f9ac2b04bd70ea5261039663947c7cc 100644 (file)
@@ -125,7 +125,7 @@ config :pleroma, :instance,
   banner_upload_limit: 4_000_000,
   registrations_open: true,
   federating: true,
-  federation_reachability_timeout_days: 90,
+  federation_reachability_timeout_days: 7,
   allow_relay: true,
   rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
   public: true,
index 0b08f0eb89c09fe279c75582fcbd61a6f09f5f6f..5e107f4c957afcc899a7ae581410f713d2689b64 100644 (file)
@@ -3,14 +3,17 @@ defmodule Pleroma.Instances do
 
   @adapter Pleroma.Instances.Instance
 
-  defdelegate filter_reachable(urls), to: @adapter
-  defdelegate reachable?(url), to: @adapter
-  defdelegate set_reachable(url), to: @adapter
-  defdelegate set_unreachable(url, unreachable_since \\ nil), to: @adapter
+  defdelegate filter_reachable(urls_or_hosts), to: @adapter
+  defdelegate reachable?(url_or_host), to: @adapter
+  defdelegate set_reachable(url_or_host), to: @adapter
+  defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter
+
+  def set_consistently_unreachable(url_or_host),
+    do: set_unreachable(url_or_host, reachability_datetime_threshold())
 
   def reachability_datetime_threshold do
     federation_reachability_timeout_days =
-      Pleroma.Config.get(:instance)[:federation_reachability_timeout_days] || 90
+      Pleroma.Config.get(:instance)[:federation_reachability_timeout_days] || 0
 
     if federation_reachability_timeout_days > 0 do
       NaiveDateTime.add(
index e3af4a8a718aa67ebe724aa4868467f01498b044..a87590d8b2e457bc7e6ab0edb8d266bfb49cb4e3 100644 (file)
@@ -17,7 +17,7 @@ defmodule Pleroma.Instances.Instance do
     timestamps()
   end
 
-  defdelegate host(url), to: Instances
+  defdelegate host(url_or_host), to: Instances
 
   def changeset(struct, params \\ %{}) do
     struct
@@ -28,9 +28,9 @@ defmodule Pleroma.Instances.Instance do
 
   def filter_reachable([]), do: []
 
-  def filter_reachable(urls) when is_list(urls) do
+  def filter_reachable(urls_or_hosts) when is_list(urls_or_hosts) do
     hosts =
-      urls
+      urls_or_hosts
       |> Enum.map(&(&1 && host(&1)))
       |> Enum.filter(&(to_string(&1) != ""))
 
@@ -44,14 +44,14 @@ defmodule Pleroma.Instances.Instance do
         )
       )
 
-    Enum.filter(urls, &(&1 && host(&1) not in unreachable_hosts))
+    Enum.filter(urls_or_hosts, &(&1 && host(&1) not in unreachable_hosts))
   end
 
-  def reachable?(url) when is_binary(url) do
+  def reachable?(url_or_host) when is_binary(url_or_host) do
     !Repo.one(
       from(i in Instance,
         where:
-          i.host == ^host(url) and
+          i.host == ^host(url_or_host) and
             i.unreachable_since <= ^Instances.reachability_datetime_threshold(),
         select: true
       )
@@ -60,8 +60,8 @@ defmodule Pleroma.Instances.Instance do
 
   def reachable?(_), do: true
 
-  def set_reachable(url) when is_binary(url) do
-    with host <- host(url),
+  def set_reachable(url_or_host) when is_binary(url_or_host) do
+    with host <- host(url_or_host),
          %Instance{} = existing_record <- Repo.get_by(Instance, %{host: host}) do
       {:ok, _instance} =
         existing_record
@@ -70,13 +70,13 @@ defmodule Pleroma.Instances.Instance do
     end
   end
 
-  def set_reachable(_), do: {0, :noop}
+  def set_reachable(_), do: {:error, nil}
 
-  def set_unreachable(url, unreachable_since \\ nil)
+  def set_unreachable(url_or_host, unreachable_since \\ nil)
 
-  def set_unreachable(url, unreachable_since) when is_binary(url) do
+  def set_unreachable(url_or_host, unreachable_since) when is_binary(url_or_host) do
     unreachable_since = unreachable_since || DateTime.utc_now()
-    host = host(url)
+    host = host(url_or_host)
     existing_record = Repo.get_by(Instance, %{host: host})
 
     changes = %{unreachable_since: unreachable_since}
@@ -89,7 +89,7 @@ defmodule Pleroma.Instances.Instance do
 
       existing_record.unreachable_since &&
           NaiveDateTime.compare(existing_record.unreachable_since, unreachable_since) != :gt ->
-        {:noop, existing_record}
+        {:ok, existing_record}
 
       true ->
         existing_record
@@ -98,5 +98,5 @@ defmodule Pleroma.Instances.Instance do
     end
   end
 
-  def set_unreachable(_, _), do: {0, :noop}
+  def set_unreachable(_, _), do: {:error, nil}
 end
index 4232d6c0a6bc5783d8aee01892dc0b6c9e9459b4..6cad02da69e8b5d61f4d30a0e59a954f95d36e4f 100644 (file)
@@ -750,9 +750,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
       Instances.set_reachable(inbox)
       result
     else
-      e ->
+      {_post_result, response} ->
         Instances.set_unreachable(inbox)
-        e
+        {:error, response}
     end
   end
 
index 80023127c80a768b94c34e86f8f010e7230c9f98..e96455423a7abac7549e727386bf0ec50882a476 100644 (file)
@@ -181,10 +181,6 @@ defmodule Pleroma.Web.Salmon do
       Logger.debug(fn -> "Pushed to #{url}, code #{code}" end)
       :ok
     else
-      {:reachable, false} ->
-        Logger.debug(fn -> "Pushing Salmon to #{url} skipped as marked unreachable)" end)
-        :noop
-
       e ->
         Instances.set_unreachable(url)
         Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end)
index 64eba7221e54ed667454219d700261ec33636e9e..abe1482702fac01a1c75d42a6e179ffc312bf952 100644 (file)
@@ -286,14 +286,10 @@ defmodule Pleroma.Web.Websub do
       Logger.info(fn -> "Pushed to #{callback}, code #{code}" end)
       {:ok, code}
     else
-      {:reachable, false} ->
-        Logger.debug(fn -> "Pushing to #{callback} skipped as marked unreachable)" end)
-        {:error, :noop}
-
-      e ->
+      {_post_result, response} ->
         Instances.set_unreachable(callback)
-        Logger.debug(fn -> "Couldn't push to #{callback}, #{inspect(e)}" end)
-        {:error, e}
+        Logger.debug(fn -> "Couldn't push to #{callback}, #{inspect(response)}" end)
+        {:error, response}
     end
   end
 end
index 964b2b61c6b8354cfebf2c5ae5bb832dee819e37..0c21093cef5c3f7c4a8082d29a7f2d49623ffa66 100644 (file)
@@ -220,4 +220,11 @@ defmodule Pleroma.Factory do
       client_secret: "aaa;/&bbb"
     }
   end
+
+  def instance_factory do
+    %Pleroma.Instances.Instance{
+      host: "domain.com",
+      unreachable_since: nil
+    }
+  end
 end
index e4279e14d5377477dd4e6357aab4097fcc003b74..3d6efd52c2e999ff14ee48fa9012785703325a19 100644 (file)
@@ -653,6 +653,14 @@ defmodule HttpRequestMock do
     {:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)}
   end
 
+  def get("http://404.site" <> _, _, _, _) do
+    {:ok,
+     %Tesla.Env{
+       status: 404,
+       body: ""
+     }}
+  end
+
   def get(url, query, body, headers) do
     {:error,
      "Not implemented the mock response for get #{inspect(url)}, #{query}, #{inspect(body)}, #{
@@ -673,6 +681,26 @@ defmodule HttpRequestMock do
      }}
   end
 
+  def post("http://200.site" <> _, _, _, _) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: ""
+     }}
+  end
+
+  def post("http://connrefused.site" <> _, _, _, _) do
+    {:error, :connrefused}
+  end
+
+  def post("http://404.site" <> _, _, _, _) do
+    {:ok,
+     %Tesla.Env{
+       status: 404,
+       body: ""
+     }}
+  end
+
   def post(url, _query, _body, _headers) do
     {:error, "Not implemented the mock response for post #{inspect(url)}"}
   end
index 1b704330f0bfa159d1c24352578e6553a8c954c3..eca5c134dc52a072abefeb3ef78591f54ee244d3 100644 (file)
@@ -146,7 +146,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it clears `unreachable` federation status of the sender", %{conn: conn} do
       sender_url = "https://pleroma.soykaf.com"
-      Instances.set_unreachable(sender_url, Instances.reachability_datetime_threshold())
+      Instances.set_consistently_unreachable(sender_url)
       refute Instances.reachable?(sender_url)
 
       data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
@@ -211,7 +211,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
 
     test "it clears `unreachable` federation status of the sender", %{conn: conn} do
       sender_host = "pleroma.soykaf.com"
-      Instances.set_unreachable(sender_host, Instances.reachability_datetime_threshold())
+      Instances.set_consistently_unreachable(sender_host)
       refute Instances.reachable?(sender_host)
 
       user = insert(:user)
index 18f0943794dad676e502737390cd44a8a0d61e8c..d517c7aa7b59b9b5c508638416d428867597aac9 100644 (file)
@@ -7,11 +7,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.{Activity, Object, User}
+  alias Pleroma.{Activity, Object, User, Instances}
   alias Pleroma.Builders.ActivityBuilder
 
   import Pleroma.Factory
   import Tesla.Mock
+  import Mock
 
   setup do
     mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -659,6 +660,46 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     assert 3 = length(activities)
   end
 
+  describe "publish_one/1" do
+    test_with_mock "it calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
+                   Instances,
+                   [:passthrough],
+                   [] do
+      actor = insert(:user)
+      inbox = "http://404.site/users/nick1/inbox"
+
+      assert {:error, _} =
+               ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
+
+      assert called(Instances.set_unreachable(inbox))
+    end
+
+    test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
+                   Instances,
+                   [:passthrough],
+                   [] do
+      actor = insert(:user)
+      inbox = "http://connrefused.site/users/nick1/inbox"
+
+      assert {:error, _} =
+               ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
+
+      assert called(Instances.set_unreachable(inbox))
+    end
+
+    test_with_mock "it does NOT call `Instances.set_unreachable` if target is reachable",
+                   Instances,
+                   [:passthrough],
+                   [] do
+      actor = insert(:user)
+      inbox = "http://200.site/users/nick1/inbox"
+
+      assert {:ok, _} = ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
+
+      refute called(Instances.set_unreachable(inbox))
+    end
+  end
+
   def data_uri do
     File.read!("test/fixtures/avatar_data_uri")
   end
index f4234aea8785e8302b996a42cc3fe82dca6b965b..c6d10ef7809edcd6a47d964fac488b3aacfc3def 100644 (file)
@@ -128,7 +128,7 @@ defmodule Pleroma.Web.FederatorTest do
           callback: "https://pleroma2.soykaf.com/cb"
         })
 
-      Instances.set_unreachable(sub1.callback, Instances.reachability_datetime_threshold())
+      Instances.set_consistently_unreachable(sub1.callback)
 
       {:ok, _activity} = CommonAPI.post(user, %{"status" => "HI"})
 
@@ -158,7 +158,7 @@ defmodule Pleroma.Web.FederatorTest do
           info: %{salmon: "https://domain2.com/salmon"}
         })
 
-      Instances.set_unreachable("domain.com", Instances.reachability_datetime_threshold())
+      Instances.set_consistently_unreachable("domain.com")
 
       {:ok, _activity} =
         CommonAPI.post(user, %{"status" => "HI @nick1@domain.com, @nick2@domain2.com!"})
diff --git a/test/web/instances/instance_test.exs b/test/web/instances/instance_test.exs
new file mode 100644 (file)
index 0000000..a158c0a
--- /dev/null
@@ -0,0 +1,107 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Instances.InstanceTest do
+  alias Pleroma.Repo
+  alias Pleroma.Instances.Instance
+
+  use Pleroma.DataCase
+
+  import Pleroma.Factory
+
+  setup_all do
+    config_path = [:instance, :federation_reachability_timeout_days]
+    initial_setting = Pleroma.Config.get(config_path)
+
+    Pleroma.Config.put(config_path, 1)
+    on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
+
+    :ok
+  end
+
+  describe "set_reachable/1" do
+    test "clears `unreachable_since` of existing matching Instance record having non-nil `unreachable_since`" do
+      instance = insert(:instance, unreachable_since: NaiveDateTime.utc_now())
+
+      assert {:ok, instance} = Instance.set_reachable(instance.host)
+      refute instance.unreachable_since
+    end
+
+    test "keeps nil `unreachable_since` of existing matching Instance record having nil `unreachable_since`" do
+      instance = insert(:instance, unreachable_since: nil)
+
+      assert {:ok, instance} = Instance.set_reachable(instance.host)
+      refute instance.unreachable_since
+    end
+
+    test "does NOT create an Instance record in case of no existing matching record" do
+      host = "domain.org"
+      assert nil == Instance.set_reachable(host)
+
+      assert [] = Repo.all(Ecto.Query.from(i in Instance))
+      assert Instance.reachable?(host)
+    end
+  end
+
+  describe "set_unreachable/1" do
+    test "creates new record having `unreachable_since` to current time if record does not exist" do
+      assert {:ok, instance} = Instance.set_unreachable("https://domain.com/path")
+
+      instance = Repo.get(Instance, instance.id)
+      assert instance.unreachable_since
+      assert "domain.com" == instance.host
+    end
+
+    test "sets `unreachable_since` of existing record having nil `unreachable_since`" do
+      instance = insert(:instance, unreachable_since: nil)
+      refute instance.unreachable_since
+
+      assert {:ok, _} = Instance.set_unreachable(instance.host)
+
+      instance = Repo.get(Instance, instance.id)
+      assert instance.unreachable_since
+    end
+
+    test "does NOT modify `unreachable_since` value of existing record in case it's present" do
+      instance =
+        insert(:instance, unreachable_since: NaiveDateTime.add(NaiveDateTime.utc_now(), -10))
+
+      assert instance.unreachable_since
+      initial_value = instance.unreachable_since
+
+      assert {:ok, _} = Instance.set_unreachable(instance.host)
+
+      instance = Repo.get(Instance, instance.id)
+      assert initial_value == instance.unreachable_since
+    end
+  end
+
+  describe "set_unreachable/2" do
+    test "sets `unreachable_since` value of existing record in case it's newer than supplied value" do
+      instance =
+        insert(:instance, unreachable_since: NaiveDateTime.add(NaiveDateTime.utc_now(), -10))
+
+      assert instance.unreachable_since
+
+      past_value = NaiveDateTime.add(NaiveDateTime.utc_now(), -100)
+      assert {:ok, _} = Instance.set_unreachable(instance.host, past_value)
+
+      instance = Repo.get(Instance, instance.id)
+      assert past_value == instance.unreachable_since
+    end
+
+    test "does NOT modify `unreachable_since` value of existing record in case it's equal to or older than supplied value" do
+      instance =
+        insert(:instance, unreachable_since: NaiveDateTime.add(NaiveDateTime.utc_now(), -10))
+
+      assert instance.unreachable_since
+      initial_value = instance.unreachable_since
+
+      assert {:ok, _} = Instance.set_unreachable(instance.host, NaiveDateTime.utc_now())
+
+      instance = Repo.get(Instance, instance.id)
+      assert initial_value == instance.unreachable_since
+    end
+  end
+end
diff --git a/test/web/instances/instances_test.exs b/test/web/instances/instances_test.exs
new file mode 100644 (file)
index 0000000..a2fdf10
--- /dev/null
@@ -0,0 +1,94 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.InstancesTest do
+  alias Pleroma.Instances
+
+  use Pleroma.DataCase
+
+  setup_all do
+    config_path = [:instance, :federation_reachability_timeout_days]
+    initial_setting = Pleroma.Config.get(config_path)
+
+    Pleroma.Config.put(config_path, 1)
+    on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
+
+    :ok
+  end
+
+  describe "reachable?/1" do
+    test "returns `true` for host / url with unknown reachability status" do
+      assert Instances.reachable?("unknown.site")
+      assert Instances.reachable?("http://unknown.site")
+    end
+
+    test "returns `false` for host / url marked unreachable for at least `reachability_datetime_threshold()`" do
+      host = "consistently-unreachable.name"
+      Instances.set_consistently_unreachable(host)
+
+      refute Instances.reachable?(host)
+      refute Instances.reachable?("http://#{host}/path")
+    end
+
+    test "returns `true` for host / url marked unreachable for less than `reachability_datetime_threshold()`" do
+      url = "http://eventually-unreachable.name/path"
+
+      Instances.set_unreachable(url)
+
+      assert Instances.reachable?(url)
+      assert Instances.reachable?(URI.parse(url).host)
+    end
+  end
+
+  describe "filter_reachable/1" do
+    test "keeps only reachable elements of supplied list" do
+      host = "consistently-unreachable.name"
+      url1 = "http://eventually-unreachable.com/path"
+      url2 = "http://domain.com/path"
+
+      Instances.set_consistently_unreachable(host)
+      Instances.set_unreachable(url1)
+
+      assert [url1, url2] == Instances.filter_reachable([host, url1, url2])
+    end
+  end
+
+  describe "set_reachable/1" do
+    test "sets unreachable url or host reachable" do
+      host = "domain.com"
+      Instances.set_consistently_unreachable(host)
+      refute Instances.reachable?(host)
+
+      Instances.set_reachable(host)
+      assert Instances.reachable?(host)
+    end
+
+    test "keeps reachable url or host reachable" do
+      url = "https://site.name?q="
+      assert Instances.reachable?(url)
+
+      Instances.set_reachable(url)
+      assert Instances.reachable?(url)
+    end
+  end
+
+  describe "set_consistently_unreachable/1" do
+    test "sets reachable url or host unreachable" do
+      url = "http://domain.com?q="
+      assert Instances.reachable?(url)
+
+      Instances.set_consistently_unreachable(url)
+      refute Instances.reachable?(url)
+    end
+
+    test "keeps unreachable url or host unreachable" do
+      host = "site.name"
+      Instances.set_consistently_unreachable(host)
+      refute Instances.reachable?(host)
+
+      Instances.set_consistently_unreachable(host)
+      refute Instances.reachable?(host)
+    end
+  end
+end
index ca447aa5d95fe69beeb1706d26561a7fc24be6df..ad9bc418a7391ed9fd917a4708328ded23d5792f 100644 (file)
@@ -62,7 +62,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
 
     test "it clears `unreachable` federation status of the sender", %{conn: conn} do
       sender_url = "https://pleroma.soykaf.com"
-      Instances.set_unreachable(sender_url, Instances.reachability_datetime_threshold())
+      Instances.set_consistently_unreachable(sender_url)
       refute Instances.reachable?(sender_url)
 
       user = insert(:user)
index c445ed676e8b3aa82f1c6632106e496212cbb899..cb19d6fe60e87f73ae2bd3f0532f712f743ca510 100644 (file)
@@ -85,7 +85,7 @@ defmodule Pleroma.Web.Websub.WebsubControllerTest do
 
     test "it clears `unreachable` federation status of the sender", %{conn: conn} do
       sender_url = "https://pleroma.soykaf.com"
-      Instances.set_unreachable(sender_url, Instances.reachability_datetime_threshold())
+      Instances.set_consistently_unreachable(sender_url)
       refute Instances.reachable?(sender_url)
 
       websub = insert(:websub_client_subscription)