Merge remote-tracking branch 'remotes/upstream/develop' into 1335-user-api-id-fields-relations

# Conflicts:
#	mix.lock
This commit is contained in:
Ivan Tashkinov 2019-11-29 10:17:59 +03:00
commit 52cc7de82c
18 changed files with 397 additions and 110 deletions

View file

@ -0,0 +1,47 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Integration.FederationTest do
use Pleroma.DataCase
@moduletag :federated
import Pleroma.Cluster
setup_all do
Pleroma.Cluster.spawn_default_cluster()
:ok
end
@federated1 :"federated1@127.0.0.1"
describe "federated cluster primitives" do
test "within/2 captures local bindings and executes block on remote node" do
captured_binding = :captured
result =
within @federated1 do
user = Pleroma.Factory.insert(:user)
{captured_binding, node(), user}
end
assert {:captured, @federated1, user} = result
refute Pleroma.User.get_by_id(user.id)
assert user.id == within(@federated1, do: Pleroma.User.get_by_id(user.id)).id
end
test "runs webserver on customized port" do
{nickname, url, url_404} =
within @federated1 do
import Pleroma.Web.Router.Helpers
user = Pleroma.Factory.insert(:user)
user_url = account_url(Pleroma.Web.Endpoint, :show, user)
url_404 = account_url(Pleroma.Web.Endpoint, :show, "not-exists")
{user.nickname, user_url, url_404}
end
assert {:ok, {{_, 200, _}, _headers, body}} = :httpc.request(~c"#{url}")
assert %{"acct" => ^nickname} = Jason.decode!(body)
assert {:ok, {{_, 404, _}, _headers, _body}} = :httpc.request(~c"#{url_404}")
end
end
end

218
test/support/cluster.ex Normal file
View file

@ -0,0 +1,218 @@
defmodule Pleroma.Cluster do
@moduledoc """
Facilities for managing a cluster of slave VM's for federated testing.
## Spawning the federated cluster
`spawn_cluster/1` spawns a map of slave nodes that are started
within the running VM. During startup, the slave node is sent all configuration
from the parent node, as well as all code. After receiving configuration and
code, the slave then starts all applications currently running on the parent.
The configuration passed to `spawn_cluster/1` overrides any parent application
configuration for the provided OTP app and key. This is useful for customizing
the Ecto database, Phoenix webserver ports, etc.
For example, to start a single federated VM named ":federated1", with the
Pleroma Endpoint running on port 4123, and with a database named
"pleroma_test1", you would run:
endpoint_conf = Application.fetch_env!(:pleroma, Pleroma.Web.Endpoint)
repo_conf = Application.fetch_env!(:pleroma, Pleroma.Repo)
Pleroma.Cluster.spawn_cluster(%{
:"federated1@127.0.0.1" => [
{:pleroma, Pleroma.Repo, Keyword.merge(repo_conf, database: "pleroma_test1")},
{:pleroma, Pleroma.Web.Endpoint,
Keyword.merge(endpoint_conf, http: [port: 4011], url: [port: 4011], server: true)}
]
})
*Note*: application configuration for a given key is not merged,
so any customization requires first fetching the existing values
and merging yourself by providing the merged configuration,
such as above with the endpoint config and repo config.
## Executing code within a remote node
Use the `within/2` macro to execute code within the context of a remote
federated node. The code block captures all local variable bindings from
the parent's context and returns the result of the expression after executing
it on the remote node. For example:
import Pleroma.Cluster
parent_value = 123
result =
within :"federated1@127.0.0.1" do
{node(), parent_value}
end
assert result == {:"federated1@127.0.0.1, 123}
*Note*: while local bindings are captured and available within the block,
other parent contexts like required, aliased, or imported modules are not
in scope. Those will need to be reimported/aliases/required within the block
as `within/2` is a remote procedure call.
"""
@extra_apps Pleroma.Mixfile.application()[:extra_applications]
@doc """
Spawns the default Pleroma federated cluster.
Values before may be customized as needed for the test suite.
"""
def spawn_default_cluster do
endpoint_conf = Application.fetch_env!(:pleroma, Pleroma.Web.Endpoint)
repo_conf = Application.fetch_env!(:pleroma, Pleroma.Repo)
spawn_cluster(%{
:"federated1@127.0.0.1" => [
{:pleroma, Pleroma.Repo, Keyword.merge(repo_conf, database: "pleroma_test_federated1")},
{:pleroma, Pleroma.Web.Endpoint,
Keyword.merge(endpoint_conf, http: [port: 4011], url: [port: 4011], server: true)}
],
:"federated2@127.0.0.1" => [
{:pleroma, Pleroma.Repo, Keyword.merge(repo_conf, database: "pleroma_test_federated2")},
{:pleroma, Pleroma.Web.Endpoint,
Keyword.merge(endpoint_conf, http: [port: 4012], url: [port: 4012], server: true)}
]
})
end
@doc """
Spawns a configured map of federated nodes.
See `Pleroma.Cluster` module documentation for details.
"""
def spawn_cluster(node_configs) do
# Turn node into a distributed node with the given long name
:net_kernel.start([:"primary@127.0.0.1"])
# Allow spawned nodes to fetch all code from this node
{:ok, _} = :erl_boot_server.start([])
allow_boot("127.0.0.1")
silence_logger_warnings(fn ->
node_configs
|> Enum.map(&Task.async(fn -> start_slave(&1) end))
|> Enum.map(&Task.await(&1, 60_000))
end)
end
@doc """
Executes block of code again remote node.
See `Pleroma.Cluster` module documentation for details.
"""
defmacro within(node, do: block) do
quote do
rpc(unquote(node), unquote(__MODULE__), :eval_quoted, [
unquote(Macro.escape(block)),
binding()
])
end
end
@doc false
def eval_quoted(block, binding) do
{result, _binding} = Code.eval_quoted(block, binding, __ENV__)
result
end
defp start_slave({node_host, override_configs}) do
log(node_host, "booting federated VM")
{:ok, node} = :slave.start(~c"127.0.0.1", node_name(node_host), vm_args())
add_code_paths(node)
load_apps_and_transfer_configuration(node, override_configs)
ensure_apps_started(node)
{:ok, node}
end
def rpc(node, module, function, args) do
:rpc.block_call(node, module, function, args)
end
defp vm_args do
~c"-loader inet -hosts 127.0.0.1 -setcookie #{:erlang.get_cookie()}"
end
defp allow_boot(host) do
{:ok, ipv4} = :inet.parse_ipv4_address(~c"#{host}")
:ok = :erl_boot_server.add_slave(ipv4)
end
defp add_code_paths(node) do
rpc(node, :code, :add_paths, [:code.get_path()])
end
defp load_apps_and_transfer_configuration(node, override_configs) do
Enum.each(Application.loaded_applications(), fn {app_name, _, _} ->
app_name
|> Application.get_all_env()
|> Enum.each(fn {key, primary_config} ->
rpc(node, Application, :put_env, [app_name, key, primary_config, [persistent: true]])
end)
end)
Enum.each(override_configs, fn {app_name, key, val} ->
rpc(node, Application, :put_env, [app_name, key, val, [persistent: true]])
end)
end
defp log(node, msg), do: IO.puts("[#{node}] #{msg}")
defp ensure_apps_started(node) do
loaded_names = Enum.map(Application.loaded_applications(), fn {name, _, _} -> name end)
app_names = @extra_apps ++ (loaded_names -- @extra_apps)
rpc(node, Application, :ensure_all_started, [:mix])
rpc(node, Mix, :env, [Mix.env()])
rpc(node, __MODULE__, :prepare_database, [])
log(node, "starting application")
Enum.reduce(app_names, MapSet.new(), fn app, loaded ->
if Enum.member?(loaded, app) do
loaded
else
{:ok, started} = rpc(node, Application, :ensure_all_started, [app])
MapSet.union(loaded, MapSet.new(started))
end
end)
end
@doc false
def prepare_database do
log(node(), "preparing database")
repo_config = Application.get_env(:pleroma, Pleroma.Repo)
repo_config[:adapter].storage_down(repo_config)
repo_config[:adapter].storage_up(repo_config)
{:ok, _, _} =
Ecto.Migrator.with_repo(Pleroma.Repo, fn repo ->
Ecto.Migrator.run(repo, :up, log: false, all: true)
end)
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
{:ok, _} = Application.ensure_all_started(:ex_machina)
end
defp silence_logger_warnings(func) do
prev_level = Logger.level()
Logger.configure(level: :error)
res = func.()
Logger.configure(level: prev_level)
res
end
defp node_name(node_host) do
node_host
|> to_string()
|> String.split("@")
|> Enum.at(0)
|> String.to_atom()
end
end

View file

@ -3,7 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only
os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: []
ExUnit.start(exclude: os_exclude)
ExUnit.start(exclude: [:federated | os_exclude])
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
Mox.defmock(Pleroma.ReverseProxy.ClientMock, for: Pleroma.ReverseProxy.Client)
{:ok, _} = Application.ensure_all_started(:ex_machina)

View file

@ -1011,9 +1011,9 @@ defmodule Pleroma.UserTest do
{:ok, user} = User.follow(user, user2)
{:ok, _user} = User.deactivate(user)
info = User.get_cached_user_info(user2)
user2 = User.get_cached_by_id(user2.id)
assert info.follower_count == 0
assert user2.follower_count == 0
assert [] = User.get_followers(user2)
end
@ -1027,10 +1027,10 @@ defmodule Pleroma.UserTest do
{:ok, _user} = User.deactivate(user)
info = User.get_cached_user_info(user2)
user2 = User.get_cached_by_id(user2.id)
assert refresh_record(user2).following_count == 0
assert info.following_count == 0
assert user2.following_count == 0
assert User.following_count(user2) == 0
assert [] = User.get_friends(user2)
end
@ -1232,13 +1232,12 @@ defmodule Pleroma.UserTest do
describe "caching" do
test "invalidate_cache works" do
user = insert(:user)
_user_info = User.get_cached_user_info(user)
User.set_cache(user)
User.invalidate_cache(user)
{:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
{:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}")
{:ok, nil} = Cachex.get(:user_cache, "user_info:#{user.id}")
end
test "User.delete() plugs any possible zombie objects" do
@ -1395,7 +1394,7 @@ defmodule Pleroma.UserTest do
{:ok, _user_relationship} = User.block(user, follower)
user = refresh_record(user)
assert User.user_info(user).follower_count == 2
assert user.follower_count == 2
end
describe "list_inactive_users_query/1" do
@ -1572,51 +1571,6 @@ defmodule Pleroma.UserTest do
end
end
describe "set_info_cache/2" do
setup do
user = insert(:user)
{:ok, user: user}
end
test "update from args", %{user: user} do
User.set_info_cache(user, %{following_count: 15, follower_count: 18})
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user)
assert followers == 18
assert following == 15
end
test "without args", %{user: user} do
User.set_info_cache(user, %{})
%{follower_count: followers, following_count: following} = User.get_cached_user_info(user)
assert followers == 0
assert following == 0
end
end
describe "user_info/2" do
setup do
user = insert(:user)
{:ok, user: user}
end
test "update from args", %{user: user} do
%{follower_count: followers, following_count: following} =
User.user_info(user, %{following_count: 15, follower_count: 18})
assert followers == 18
assert following == 15
end
test "without args", %{user: user} do
%{follower_count: followers, following_count: following} = User.user_info(user)
assert followers == 0
assert following == 0
end
end
describe "is_internal_user?/1" do
test "non-internal user returns false" do
user = insert(:user)
@ -1673,14 +1627,14 @@ defmodule Pleroma.UserTest do
ap_enabled: true
)
assert User.user_info(other_user).following_count == 0
assert User.user_info(other_user).follower_count == 0
assert other_user.following_count == 0
assert other_user.follower_count == 0
{:ok, user} = Pleroma.User.follow(user, other_user)
other_user = Pleroma.User.get_by_id(other_user.id)
assert User.user_info(user).following_count == 1
assert User.user_info(other_user).follower_count == 1
assert user.following_count == 1
assert other_user.follower_count == 1
end
test "syncronizes the counters with the remote instance for the followed when enabled" do
@ -1696,14 +1650,14 @@ defmodule Pleroma.UserTest do
ap_enabled: true
)
assert User.user_info(other_user).following_count == 0
assert User.user_info(other_user).follower_count == 0
assert other_user.following_count == 0
assert other_user.follower_count == 0
Pleroma.Config.put([:instance, :external_user_synchronization], true)
{:ok, _user} = User.follow(user, other_user)
other_user = User.get_by_id(other_user.id)
assert User.user_info(other_user).follower_count == 437
assert other_user.follower_count == 437
end
test "syncronizes the counters with the remote instance for the follower when enabled" do
@ -1719,13 +1673,13 @@ defmodule Pleroma.UserTest do
ap_enabled: true
)
assert User.user_info(other_user).following_count == 0
assert User.user_info(other_user).follower_count == 0
assert other_user.following_count == 0
assert other_user.follower_count == 0
Pleroma.Config.put([:instance, :external_user_synchronization], true)
{:ok, other_user} = User.follow(other_user, user)
assert User.user_info(other_user).following_count == 152
assert other_user.following_count == 152
end
end

View file

@ -39,6 +39,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert activity == returned_activity
end
@tag capture_log: true
test "it fetches replied-to activities if we don't have them" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
@ -533,6 +534,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert object.data["content"] == "this is a private toot"
end
@tag capture_log: true
test "it rejects incoming announces with an inlined activity from another origin" do
data =
File.read!("test/fixtures/bogus-mastodon-announce.json")
@ -814,6 +816,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert Activity.get_by_id(activity.id)
end
@tag capture_log: true
test "it works for incoming user deletes" do
%{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
@ -1749,6 +1752,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert modified_object["inReplyToAtomUri"] == ""
end
@tag capture_log: true
test "returns modified object when allowed incoming reply", %{data: data} do
object_with_reply =
Map.put(
@ -1868,6 +1872,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
end) =~ "Unsupported URI scheme"
end
@tag capture_log: true
test "returns {:ok, %Object{}} for success case" do
assert {:ok, %Object{}} =
Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")

View file

@ -1923,6 +1923,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
Pleroma.Config.put([:instance, :dynamic_configuration], true)
end
@tag capture_log: true
test "create new config setting in db", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{

View file

@ -350,7 +350,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
}
}
assert expected == AccountView.render("show.json", %{user: user, for: other_user})
assert expected ==
AccountView.render("show.json", %{user: refresh_record(user), for: other_user})
end
test "returns the settings store if the requesting user is the represented user and it's requested specifically" do
@ -374,6 +375,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
refute result.display_name == "<marquee> username </marquee>"
end
test "never display nil user follow counts" do
user = insert(:user, following_count: 0, follower_count: 0)
result = AccountView.render("show.json", %{user: user})
assert result.following_count == 0
assert result.followers_count == 0
end
describe "hiding follows/following" do
test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do
user =

View file

@ -15,7 +15,7 @@ defmodule Pleroma.Web.StreamerTest do
alias Pleroma.Web.Streamer.StreamerSocket
alias Pleroma.Web.Streamer.Worker
@moduletag needs_streamer: true
@moduletag needs_streamer: true, capture_log: true
clear_config_all([:instance, :skip_thread_containment])
describe "user streams" do