Remove elixir tests

Our regular suite cover most of those cases, and it get trickier and
tricker to keep those working after changes in ejabberd.
This commit is contained in:
Paweł Chmielowski 2018-12-21 15:56:56 +01:00
parent 740ea3a047
commit 51cbbf313f
14 changed files with 0 additions and 2105 deletions

View File

@ -1,389 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule ACLTest do
@author "mremond@process-one.net"
use ExUnit.Case, async: false
setup_all do
:ok = :mnesia.start
:ejabberd_mnesia.start
:jid.start
:ejabberd_hooks.start_link
:stringprep.start
:ok = :ejabberd_config.start(["domain1", "domain2"], [])
{:ok, _} = :acl.start_link
:ok
end
setup do
:acl.clear
end
test "access rule match with user part ACL" do
:acl.add(:global, :basic_acl_1, {:user, "test1"})
:acl.add(:global, :basic_acl_1, {:user, "test2"})
:acl.add_access(:global, :basic_rule_1, [{:allow, [{:acl, :basic_acl_1}]}])
# JID can only be passes as jid record.
# => TODO: Support passing JID as binary.
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test1@domain1")) == :allow
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test1@domain2")) == :allow
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test2@domain1")) == :allow
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test2@domain2")) == :allow
# We match on user part only for local domain. As an implicit rule remote domain are not matched
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test1@otherdomain")) == :deny
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test2@otherdomain")) == :deny
assert :acl.match_rule(:global, :basic_rule_1, :jid.from_string("test11@domain1")) == :deny
:acl.add(:global, :basic_acl_2, {:user, {"test2", "domain1"}})
:acl.add_access(:global, :basic_rule_2, [{:allow, [{:acl, :basic_acl_2}]}])
assert :acl.match_rule(:global, :basic_rule_2, :jid.from_string("test2@domain1")) == :allow
assert :acl.match_rule(:global, :basic_rule_2, :jid.from_string("test2@domain2")) == :deny
assert :acl.match_rule(:global, :basic_rule_2, :jid.from_string("test2@otherdomain")) == :deny
assert :acl.match_rule(:global, :basic_rule_2, {127,0,0,1}) == :deny
end
test "IP based ACL" do
:acl.add(:global, :ip_acl_1, {:ip, "127.0.0.0/24"})
:acl.add_access(:global, :ip_rule_1, [{:allow, [{:acl, :ip_acl_1}]}])
# IP must be expressed as a tuple when calling match rule
assert :acl.match_rule(:global, :ip_rule_1, {127,0,0,1}) == :allow
assert :acl.match_rule(:global, :ip_rule_1, {127,0,1,1}) == :deny
assert :acl.match_rule(:global, :ip_rule_1, :jid.from_string("test1@domain1")) == :deny
end
test "Access rule are evaluated sequentially" do
:acl.add(:global, :user_acl_1, {:user, {"test1", "domain2"}})
:acl.add(:global, :user_acl_2, {:user, "test1"})
:acl.add_access(:global, :user_rule_1, [{:deny, [{:acl, :user_acl_1}]}, {:allow, [{:acl, :user_acl_2}]}])
assert :acl.match_rule(:global, :user_rule_1, :jid.from_string("test1@domain1")) == :allow
assert :acl.match_rule(:global, :user_rule_1, :jid.from_string("test1@domain2")) == :deny
end
# Access rules are sometimes used to provide values (i.e.: max_s2s_connections, max_user_sessions)
test "Access rules providing values" do
:acl.add(:global, :user_acl, {:user_regexp, ""})
:acl.add(:global, :admin_acl, {:user, "admin"})
:acl.add_access(:global, :value_rule_1, [{10, [{:acl, :admin_acl}]}, {5, [{:acl, :user_acl}]}])
assert :acl.match_rule(:global, :value_rule_1, :jid.from_string("test1@domain1")) == 5
assert :acl.match_rule(:global, :value_rule_1, :jid.from_string("admin@domain1")) == 10
# If we have no match, :deny is still the default value
# => TODO maybe we should have a match rule which allow passing custom default value ?
assert :acl.match_rule(:global, :value_rule_1, :jid.from_string("user@otherdomain")) == :deny
end
# At the moment IP and user rules to no go well together: There is
# no way to combine IP and user restrictions.
# => TODO we need to implement access rules that implement both and will deny the access
# if either IP or user returns deny
test "mixing IP and user access rules" do
:acl.add(:global, :user_acl_1, {:user, "test1"})
:acl.add(:global, :ip_acl_1, {:ip, "127.0.0.0/24"})
:acl.add_access(:global, :mixed_rule_1, [{:allow, [{:acl, :user_acl_1}]}, {:allow, [{:acl, :ip_acl_1}]}])
assert :acl.match_rule(:global, :mixed_rule_1, :jid.from_string("test1@domain1")) == :allow
assert :acl.match_rule(:global, :mixed_rule_1, {127,0,0,1}) == :allow
:acl.add_access(:global, :mixed_rule_2, [{:deny, [{:acl, :user_acl_1}]}, {:allow, [{:acl, :ip_acl_1}]}])
assert :acl.match_rule(:global, :mixed_rule_2, :jid.from_string("test1@domain1")) == :deny
assert :acl.match_rule(:global, :mixed_rule_2, {127,0,0,1}) == :allow
end
test "access_matches works with predefined access rules" do
:acl.add(:global, :user_acl_2, {:user, "user"})
:acl.add_access(:global, :user_rule_2, [{:allow, [{:acl, :user_acl_2}]}, {:deny, [:all]}])
assert :acl.access_matches(:user_rule_2, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
assert :acl.access_matches(:user_rule_2, %{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
end
test "access_matches rule all always matches" do
assert :acl.access_matches(:all, %{}, :global) == :allow
assert :acl.access_matches(:all, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
end
test "access_matches rule none never matches" do
assert :acl.access_matches(:none, %{}, :global) == :deny
assert :acl.access_matches(:none, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
end
test "access_matches with not existing rule never matches" do
assert :acl.access_matches(:bleble, %{}, :global) == :deny
assert :acl.access_matches(:bleble, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
end
test "access_matches works with inlined access rules" do
:acl.add(:global, :user_acl_3, {:user, "user"})
assert :acl.access_matches([{:allow, [{:acl, :user_acl_3}]}, {:deny, [:all]}],
%{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
assert :acl.access_matches([{:allow, [{:acl, :user_acl_3}]}, {:deny, [:all]}],
%{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
end
test "access_matches allow to have acl rules inlined" do
assert :acl.access_matches([{:allow, [{:user, "user"}]}, {:deny, [:all]}],
%{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
assert :acl.access_matches([{:allow, [{:user, "user"}]}, {:deny, [:all]}],
%{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
end
test "access_matches test have implicit deny at end" do
assert :acl.access_matches([{:allow, [{:user, "user"}]}],
%{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
assert :acl.access_matches([{:allow, [{:user, "user"}]}],
%{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
end
test "access_matches requires that all subrules match" do
rules = [{:allow, [{:user, "user"}, {:ip, {{127,0,0,1}, 32}}]}]
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,2}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
end
test "access_matches rules are matched in order" do
rules = [{:allow, [{:user, "user"}]}, {:deny, [{:user, "user2"}]}, {:allow, [{:user_regexp, "user"}]}]
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user2", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user22", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
end
test "access_matches rules that require ip but no one is provided don't crash" do
rules = [{:allow, [{:ip, {{127,0,0,1}, 32}}]},
{:allow, [{:user, "user"}]},
{:allow, [{:user, "user2"}, {:ip, {{127,0,0,1}, 32}}]}]
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user2", "domain1", ""}}, :global) == :deny
end
test "access_matches rules that require usr but no one is provided don't crash" do
rules = [{:allow, [{:ip, {{127,0,0,1}, 32}}]},
{:allow, [{:user, "user"}]},
{:allow, [{:user, "user2"}, {:ip, {{127,0,0,2}, 32}}]}]
assert :acl.access_matches(rules, %{ip: {127,0,0,1}}, :global) == :allow
assert :acl.access_matches(rules, %{ip: {127,0,0,2}}, :global) == :deny
end
test "access_matches rules with all always matches" do
rules = [{:allow, [:all]}, {:deny, {:user, "user"}}]
assert :acl.access_matches(rules, %{}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
end
test "access_matches rules with {acl, all} always matches" do
rules = [{:allow, [{:acl, :all}]}, {:deny, {:user, "user"}}]
assert :acl.access_matches(rules, %{}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :allow
end
test "access_matches rules with none never matches" do
rules = [{:allow, [:none]}, {:deny, [:all]}]
assert :acl.access_matches(rules, %{}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
end
test "access_matches with no rules never matches" do
assert :acl.access_matches([], %{}, :global) == :deny
assert :acl.access_matches([], %{usr: {"user", "domain1", ""}, ip: {127,0,0,1}}, :global) == :deny
end
test "access_matches ip rule accepts {ip, port}" do
rules = [{:allow, [{:ip, {{127,0,0,1}, 32}}]}]
assert :acl.access_matches(rules, %{ip: {{127,0,0,1}, 5000}}, :global) == :allow
assert :acl.access_matches(rules, %{ip: {{127,0,0,2}, 5000}}, :global) == :deny
end
test "access_matches user rule works" do
rules = [{:allow, [{:user, "user1"}]}]
assert :acl.access_matches(rules, %{usr: {"user1", "domain1", ""}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user2", "domain1", ""}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user1", "domain3", ""}}, :global) == :deny
end
test "access_matches 2 arg user rule works" do
rules = [{:allow, [{:user, {"user1", "server1"}}]}]
assert :acl.access_matches(rules, %{usr: {"user1", "server1", ""}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user1", "server2", ""}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user2", "server1", ""}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user2", "server2", ""}}, :global) == :deny
end
test "access_matches server rule works" do
rules = [{:allow, [{:server, "server1"}]}]
assert :acl.access_matches(rules, %{usr: {"user", "server1", ""}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user", "server2", ""}}, :global) == :deny
end
test "access_matches resource rule works" do
rules = [{:allow, [{:resource, "res1"}]}]
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "res1"}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "res2"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user", "domain3", "res1"}}, :global) == :allow
end
test "access_matches user_regexp rule works" do
rules = [{:allow, [{:user_regexp, "user[0-9]"}]}]
assert :acl.access_matches(rules, %{usr: {"user1", "domain1", "res1"}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"userA", "domain1", "res1"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user1", "domain3", "res1"}}, :global) == :deny
end
test "access_matches 2 arg user_regexp rule works" do
rules = [{:allow, [{:user_regexp, {"user[0-9]", "server1"}}]}]
assert :acl.access_matches(rules, %{usr: {"user1", "server1", "res1"}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"userA", "server1", "res1"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user1", "server2", "res1"}}, :global) == :deny
end
test "access_matches server_regexp rule works" do
rules = [{:allow, [{:server_regexp, "server[0-9]"}]}]
assert :acl.access_matches(rules, %{usr: {"user", "server1", ""}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user", "serverA", ""}}, :global) == :deny
end
test "access_matches resource_regexp rule works" do
rules = [{:allow, [{:resource_regexp, "res[0-9]"}]}]
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "res1"}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "resA"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user", "domain3", "res1"}}, :global) == :allow
end
test "access_matches node_regexp rule works" do
rules = [{:allow, [{:node_regexp, {"user[0-9]", "server[0-9]"}}]}]
assert :acl.access_matches(rules, %{usr: {"user1", "server1", "res1"}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"userA", "server1", "res1"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user1", "serverA", "res1"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"userA", "serverA", "res1"}}, :global) == :deny
end
test "access_matches user_glob rule works" do
rules = [{:allow, [{:user_glob, "user?"}]}]
assert :acl.access_matches(rules, %{usr: {"user1", "domain1", "res1"}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user11", "domain1", "res1"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user1", "domain3", "res1"}}, :global) == :deny
end
test "access_matches 2 arg user_glob rule works" do
rules = [{:allow, [{:user_glob, {"user?", "server1"}}]}]
assert :acl.access_matches(rules, %{usr: {"user1", "server1", "res1"}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user11", "server1", "res1"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user1", "server2", "res1"}}, :global) == :deny
end
test "access_matches server_glob rule works" do
rules = [{:allow, [{:server_glob, "server?"}]}]
assert :acl.access_matches(rules, %{usr: {"user", "server1", ""}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user", "server11", ""}}, :global) == :deny
end
test "access_matches resource_glob rule works" do
rules = [{:allow, [{:resource_glob, "res?"}]}]
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "res1"}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user", "domain1", "res11"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user", "domain3", "res1"}}, :global) == :allow
end
test "access_matches node_glob rule works" do
rules = [{:allow, [{:node_glob, {"user?", "server?"}}]}]
assert :acl.access_matches(rules, %{usr: {"user1", "server1", "res1"}}, :global) == :allow
assert :acl.access_matches(rules, %{usr: {"user11", "server1", "res1"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user1", "server11", "res1"}}, :global) == :deny
assert :acl.access_matches(rules, %{usr: {"user11", "server11", "res1"}}, :global) == :deny
end
test "transform_access_rules_config expands allow rule" do
assert :acl.transform_access_rules_config([:allow]) == [{:allow, [:all]}]
end
test "transform_access_rules_config expands deny rule" do
assert :acl.transform_access_rules_config([:deny]) == [{:deny, [:all]}]
end
test "transform_access_rules_config expands <integer> rule" do
assert :acl.transform_access_rules_config([100]) == [{100, [:all]}]
end
test "transform_access_rules_config expands <shaper_name> rule" do
assert :acl.transform_access_rules_config([:fast]) == [{:fast, [:all]}]
end
test "transform_access_rules_config expands allow: <acl_name> rule" do
assert :acl.transform_access_rules_config([{:allow, :test1}]) == [{:allow, [{:acl, :test1}]}]
end
test "transform_access_rules_config expands deny: <acl_name> rule" do
assert :acl.transform_access_rules_config([{:deny, :test1}]) == [{:deny, [{:acl, :test1}]}]
end
test "transform_access_rules_config expands integer: <acl_name> rule" do
assert :acl.transform_access_rules_config([{100, :test1}]) == [{100, [{:acl, :test1}]}]
end
test "transform_access_rules_config expands <shaper_name>: <acl_name> rule" do
assert :acl.transform_access_rules_config([{:fast, :test1}]) == [{:fast, [{:acl, :test1}]}]
end
test "transform_access_rules_config expands allow rule (no list)" do
assert :acl.transform_access_rules_config(:allow) == [{:allow, [:all]}]
end
test "transform_access_rules_config expands deny rule (no list)" do
assert :acl.transform_access_rules_config(:deny) == [{:deny, [:all]}]
end
test "transform_access_rules_config expands <integer> rule (no list)" do
assert :acl.transform_access_rules_config(100) == [{100, [:all]}]
end
test "transform_access_rules_config expands <shaper_name> rule (no list)" do
assert :acl.transform_access_rules_config(:fast) == [{:fast, [:all]}]
end
test "access_rules_validator works with <AccessName>" do
assert :acl.access_rules_validator(:my_access) == :my_access
end
test "shapes_rules_validator works with <AccessName>" do
assert :acl.shaper_rules_validator(:my_access) == :my_access
end
## Checking ACL on both user pattern and IP
## ========================================
# Typical example is mod_register
# Deprecated approach
test "module can test both IP and user through two independent :acl.match_rule check (deprecated)" do
:acl.add(:global, :user_acl, {:user, {"test1", "domain1"}})
:acl.add(:global, :ip_acl, {:ip, "127.0.0.0/24"})
:acl.add_access(:global, :user_rule, [{:allow, [{:acl, :user_acl}]}])
:acl.add_access(:global, :ip_rule, [{:allow, [{:acl, :ip_acl}]}])
# acl module in 16.03 is not able to provide a function for compound result:
assert :acl.match_rule(:global, :user_rule, :jid.from_string("test1@domain1")) == :allow
assert :acl.match_rule(:global, :ip_rule, {127,0,0,1}) == :allow
assert :acl.match_rule(:global, :user_rule, :jid.from_string("test2@domain1")) == :deny
assert :acl.match_rule(:global, :ip_rule, {127,0,1,1}) == :deny
end
end

View File

@ -1,88 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule EjabberdAdminTest do
use ExUnit.Case, async: false
@author "jsautret@process-one.net"
setup_all do
:mnesia.start
:ejabberd_mnesia.start
# For some myterious reason, :ejabberd_commands.init mays
# sometimes fails if module is not loaded before
:ejabberd_config.start(["domain"], [])
{:module, :ejabberd_commands} = Code.ensure_loaded(:ejabberd_commands)
:ejabberd_hooks.start_link
{:ok, _} = :acl.start_link
{:ok, _} = :ejabberd_access_permissions.start_link()
:ejabberd_commands.start_link
:ejabberd_admin.start_link
:ok
end
setup do
:ok
end
test "Logvel can be set and retrieved" do
:ejabberd_logger.start()
assert :lager == call_command(:set_loglevel, [1])
assert {1, :critical, 'Critical'} ==
call_command(:get_loglevel, [])
assert :lager == call_command(:set_loglevel, [2])
assert {2, :error, 'Error'} ==
call_command(:get_loglevel, [])
assert :lager == call_command(:set_loglevel, [3])
assert {3, :warning, 'Warning'} ==
call_command(:get_loglevel, [])
# assert {:wrong_loglevel, 6} ==
# catch_throw call_command(:set_loglevel, [6])
# assert {3, :warning, 'Warning'} ==
# call_command(:get_loglevel, [])
assert :lager == call_command(:set_loglevel, [4])
assert {4, :info, 'Info'} ==
call_command(:get_loglevel, [])
assert :lager == call_command(:set_loglevel, [5])
assert {5, :debug, 'Debug'} ==
call_command(:get_loglevel, [])
assert :lager == call_command(:set_loglevel, [0])
assert {0, :no_log, 'No log'} ==
call_command(:get_loglevel, [])
end
defp call_command(name, args) do
:ejabberd_commands.execute_command2(name, args, %{:caller_module => :ejabberd_ctl})
end
test "command status works with ejabberd stopped" do
assert :ejabberd_not_running ==
elem(call_command(:status, []), 0)
end
end

View File

@ -1,74 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule EjabberdAuthMock do
@author "jsautret@process-one.net"
@agent __MODULE__
def init do
try do
Agent.stop(@agent)
catch
:exit, _e -> :ok
end
{:ok, _pid} = Agent.start_link(fn -> %{} end, name: @agent)
mock(:ejabberd_auth, :user_exists,
fn (user, domain) ->
Agent.get(@agent, fn users -> Map.get(users, {user, domain}) end) != nil
end)
mock(:ejabberd_auth, :get_password_s,
fn (user, domain) ->
Agent.get(@agent, fn users -> Map.get(users, {user, domain}, "") end )
end)
mock(:ejabberd_auth, :check_password,
fn (user, _authzid, domain, password) ->
Agent.get(@agent, fn users ->
Map.get(users, {user, domain}) end) == password
end)
mock(:ejabberd_auth, :set_password,
fn (user, domain, password) ->
Agent.update(@agent, fn users ->
Map.put(users, {user, domain}, password) end)
end)
end
def create_user(user, domain, password) do
Agent.update(@agent, fn users -> Map.put(users, {user, domain}, password) end)
end
####################################################################
# Helpers
####################################################################
# TODO refactor: Move to ejabberd_test_mock
def mock(module, function, fun) do
try do
:meck.new(module)
catch
:error, {:already_started, _pid} -> :ok
end
:meck.expect(module, function, fun)
end
end

View File

@ -1,203 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
# Notes on the tests:
#
# This test suite will print out errors in logs for tests:
#
# test "Error in run_fold is ignored"
# test "Throw in run_fold is ignored"
# test "Exit in run_fold is ignored"
#
# Those tests are not failing and we can safely ignore those errors in
# log as we are exercising hook handler recovery from that situation.
defmodule EjabberdHooksTest do
use ExUnit.Case, async: false
@author "mremond@process-one.net"
@host <<"domain.net">>
@self __MODULE__
setup_all do
{:ok, _pid} = :ejabberd_hooks.start_link
:ok
end
setup do
:meck.unload
:true = :ejabberd_hooks.delete_all_hooks
:ok
end
test "An anonymous function can be added as a hook" do
hookname = :test_fun_hook
:ok = :ejabberd_hooks.add(hookname, @host, fn _ -> :ok end, 50)
[{50, :undefined, _}] = :ejabberd_hooks.get_handlers(hookname, @host)
end
test "A module function can be added as a hook" do
hookname = :test_mod_hook
callback = :hook_callback
:ok = :ejabberd_hooks.add(hookname, @host, @self, callback, 40)
[{40, @self, _callback}] = :ejabberd_hooks.get_handlers(hookname, @host)
end
test "An anonymous function can be removed from hook handlers" do
hookname = :test_fun_hook
anon_fun = fn _ -> :ok end
:ok = :ejabberd_hooks.add(hookname, @host, anon_fun, 50)
:ok = :ejabberd_hooks.delete(hookname, @host, anon_fun, 50)
[] = :ejabberd_hooks.get_handlers(hookname, @host)
end
test "An module function can be removed from hook handlers" do
hookname = :test_mod_hook
callback = :hook_callback
:ok = :ejabberd_hooks.add(hookname, @host, @self, callback, 40)
:ok = :ejabberd_hooks.delete(hookname, @host, @self, callback, 40)
[] = :ejabberd_hooks.get_handlers(hookname, @host)
# TODO: Check that removed function is not call anymore
end
test "'Run hook' call registered handler once" do
test_result = :hook_result
run_hook([], fn -> test_result end, test_result)
end
test "'Run hook' can call registered handler with parameters" do
test_result = :hook_result_with_params
run_hook([:hook_params], fn _ -> test_result end, test_result)
end
# TODO test "Several handlers are run in order by hook"
test "Hook run chain is stopped when handler return 'stop'" do
# setup test
hookname = :test_mod_hook
modulename = :hook_module
mock(modulename, :hook_callback1, fn _ -> :stop end)
mock(modulename, :hook_callback2, fn _ -> :end_result end)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 50)
:ok = :ejabberd_hooks.run(hookname, @host, [:hook_params])
# callback2 is never run:
[{_pid, {^modulename, _callback, [:hook_params]}, :stop}] = :meck.history(modulename)
end
test "Run fold hooks accumulate state in correct order through handlers" do
# setup test
hookname = :test_mod_hook
modulename = :hook_module
mock(modulename, :hook_callback1, fn(list, user) -> [user|list] end)
mock(modulename, :hook_callback2, fn(list, _user) -> ["jid2"|list] end)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback2, 50)
["jid2", "jid1"] = :ejabberd_hooks.run_fold(hookname, @host, [], ["jid1"])
end
test "Hook run_fold are executed based on priority order, not registration order" do
# setup test
hookname = :test_mod_hook
modulename = :hook_module
mock(modulename, :hook_callback1, fn(_acc) -> :first end)
mock(modulename, :hook_callback2, fn(_acc) -> :second end)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback2, 50)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
:second = :ejabberd_hooks.run_fold(hookname, @host, :started, [])
# Both module have been called:
2 = length(:meck.history(modulename))
end
# TODO: Test with ability to stop and return a value
test "Hook run_fold chain is stopped when handler return 'stop'" do
# setup test
hookname = :test_mod_hook
modulename = :hook_module
mock(modulename, :hook_callback1, fn(_acc) -> :stop end)
mock(modulename, :hook_callback2, fn(_acc) -> :executed end)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback2, 50)
:stopped = :ejabberd_hooks.run_fold(hookname, @host, :started, [])
# Only one module has been called
[{_pid, {^modulename, :hook_callback1, [:started]}, :stop}] = :meck.history(modulename)
end
test "Error in run_fold is ignored" do
run_fold_crash(fn(_acc) -> raise "crashed" end)
end
test "Throw in run_fold is ignored" do
run_fold_crash(fn(_acc) -> throw :crashed end)
end
test "Exit in run_fold is ignored" do
run_fold_crash(fn(_acc) -> exit :crashed end)
end
# test for run hook with various number of params
def run_hook(params, fun, result) do
# setup test
hookname = :test_mod_hook
modulename = :hook_module
callback = :hook_callback
mock(modulename, callback, fun)
# Then check
:ok = :ejabberd_hooks.add(hookname, @host, modulename, callback, 40)
:ok = :ejabberd_hooks.run(hookname, @host, params)
[{_pid, {^modulename, ^callback, ^params}, ^result}] = :meck.history(modulename)
end
def run_fold_crash(crash_fun) do
# setup test
hookname = :test_mod_hook
modulename = :hook_module
mock(modulename, :hook_callback1, crash_fun)
mock(modulename, :hook_callback2, fn(_acc) -> :final end)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback1, 40)
:ok = :ejabberd_hooks.add(hookname, @host, modulename, :hook_callback2, 50)
:final = :ejabberd_hooks.run_fold(hookname, @host, :started, [])
# Both handlers were called
2 = length(:meck.history(modulename))
end
# TODO refactor: Move to ejabberd_test_mock
def mock(module, function, fun) do
try do
:meck.new(module, [:non_strict])
catch
:error, {:already_started, _pid} -> :ok
end
:meck.expect(module, function, fun)
end
end

View File

@ -1,50 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule EjabberdOauthMock do
@author "jsautret@process-one.net"
def init() do
:mnesia.start
:mnesia.create_table(:oauth_token,
[ram_copies: [node],
attributes: [:oauth_token, :us, :scope, :expire]])
:application.start(:cache_tab)
:cache_tab.new(:oauth_token,
[{:max_size, 1000}, {:life_time, 3600}])
end
def get_token(user, domain, command, expiration \\ 3600) do
now = {megasecs, secs, _} = :os.timestamp
expire = 1000000 * megasecs + secs + expiration
:random.seed now
token = to_string :random.uniform(100000000)
{:ok, _} = :ejabberd_oauth.associate_access_token(token,
[{"resource_owner",
{:user, user, domain}},
{"scope", [to_string command]},
{"expiry_time", expire}],
[])
token
end
end

View File

@ -1,121 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule EjabberdSmMock do
@author "jsautret@process-one.net"
require Record
Record.defrecord :session, Record.extract(:session, from_lib: "ejabberd/include/ejabberd_sm.hrl")
Record.defrecord :jid, Record.extract(:jid, from_lib: "xmpp/include/jid.hrl")
@agent __MODULE__
def init do
ModLastMock.init
try do
Agent.stop(@agent)
catch
:exit, _e -> :ok
end
{:ok, _pid} = Agent.start_link(fn -> [] end, name: @agent)
mock(:ejabberd_sm, :get_user_resources,
fn (user, domain) -> for s <- get_sessions(user, domain), do: s.resource end)
mock(:ejabberd_sm, :route,
fn (to, {:exit, _reason}) ->
user = jid(to, :user)
domain = jid(to, :server)
resource = jid(to, :resource)
disconnect_resource(user, domain, resource)
:ok
(_, _) -> :ok
end)
end
def connect_resource(user, domain, resource,
opts \\ [priority: 1, conn: :c2s]) do
Agent.update(@agent, fn sessions ->
session = %{user: user, domain: domain, resource: resource,
timestamp: :os.timestamp, pid: self, node: node,
auth_module: :ejabberd_auth, ip: :undefined,
priority: opts[:priority], conn: opts[:conn]}
[session | sessions]
end)
end
def disconnect_resource(user, domain, resource) do
disconnect_resource(user, domain, resource, ModLastMock.now)
end
def disconnect_resource(user, domain, resource, timestamp) do
Agent.update(@agent, fn sessions ->
for s <- sessions,
s.user != user or s.domain != domain or s.resource != resource, do: s
end)
ModLastMock.set_last user, domain, "", timestamp
end
def get_sessions() do
Agent.get(@agent, fn sessions -> sessions end)
end
def get_sessions(user, domain) do
Agent.get(@agent, fn sessions ->
for s <- sessions, s.user == user, s.domain == domain, do: s
end)
end
def get_session(user, domain, resource) do
Agent.get(@agent, fn sessions ->
for s <- sessions,
s.user == user, s.domain == domain, s.resource == resource, do: s
end)
end
def to_record(s) do
session(usr: {s.user, s.domain, s.ressource},
us: {s.user, s.domain},
sid: {s.timestamp, s.pid},
priority: s.priority,
info: [conn: s.conn, ip: s.ip, node: s.node,
oor: false, auth_module: s.auth_module])
end
####################################################################
# Helpers
####################################################################
# TODO refactor: Move to ejabberd_test_mock
def mock(module, function, fun) do
try do
:meck.new(module)
catch
:error, {:already_started, _pid} -> :ok
end
:meck.expect(module, function, fun)
end
end

View File

@ -1,119 +0,0 @@
%%%-------------------------------------------------------------------
%%% Author : Mickael Remond <mremond@process-one.net>
%%% Created : 19 Feb 2015 by Mickael Remond <mremond@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
%%% This is a common test wrapper to run our ejabberd tests written in
%%% Elixir from standard common test code.
%%%
%%% Example: Is run with:
%%% ./rebar skip_deps=true ct suites=elixir
%%% or from ejabber overall test suite:
%%% make quicktest
-module(elixir_SUITE).
-compile(export_all).
init_per_suite(Config) ->
suite:setup_ejabberd_lib_path(Config),
check_meck(),
code:add_pathz(filename:join(test_dir(), "../include")),
Config.
end_per_suite(_Config) ->
ok.
init_per_testcase(_TestCase, Config) ->
process_flag(error_handler, ?MODULE),
Config.
all() ->
case is_elixir_available() of
true ->
Dir = test_dir(),
filelib:fold_files(Dir, ".*test\.exs$", false,
fun(Filename, Acc) -> [list_to_atom(filename:basename(Filename)) | Acc] end,
[]);
false ->
[]
end.
check_meck() ->
case catch meck:module_info(module) of
meck ->
ok;
{'EXIT',{undef, _}} ->
ct:print("meck is not available. Please make sure you configured ejabberd with --enable-elixir --enable-tools"),
ok
end.
is_elixir_available() ->
case catch elixir:module_info() of
{'EXIT',{undef,_}} ->
ct:print("ejabberd has not been build with Elixir support, skipping Elixir tests."),
false;
ModInfo when is_list(ModInfo) ->
true
end.
undefined_function(?MODULE, Func, Args) ->
case lists:suffix(".exs", atom_to_list(Func)) of
true ->
run_elixir_test(Func);
false ->
error_handler:undefined_function(?MODULE, Func, Args)
end;
undefined_function(Module, Func, Args) ->
error_handler:undefined_function(Module, Func,Args).
run_elixir_test(Func) ->
%% Elixir tests can be tagged as follow to be ignored (place before test start)
%% @tag pending: true
'Elixir.ExUnit':start([{exclude, [{pending, true}]},
{formatters,
['Elixir.ExUnit.CLIFormatter',
'Elixir.ExUnit.CTFormatter']},
{autorun, false}]),
filelib:fold_files(test_dir(), ".*mock\.exs\$", true,
fun (File, N) ->
'Elixir.Code':load_file(list_to_binary(File)),
N+1
end, 0),
'Elixir.Code':load_file(list_to_binary(filename:join(test_dir(), atom_to_list(Func)))),
%% I did not use map syntax, so that this file can still be build under R16
catch 'Elixir.ExUnit.Server':cases_loaded(),
ResultMap = 'Elixir.ExUnit':run(),
case maps:find(failures, ResultMap) of
{ok, 0} ->
%% Zero failures
ok;
{ok, Failures} ->
ct:print("Tests failed in module '~s': ~.10B failures.~nSee logs for details", [Func, Failures]),
ct:fail(elixir_test_failure),
error
end.
test_dir() ->
{ok, CWD} = file:get_cwd(),
filename:join(CWD, "../../test").

View File

@ -1,45 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule JidTest do
@author "mremond@process-one.net"
use ExUnit.Case, async: true
require Record
Record.defrecord :jid, Record.extract(:jid, from_lib: "xmpp/include/jid.hrl")
setup_all do
:stringprep.start
:jid.start
:ok
end
test "create a jid from a binary" do
jid = :jid.from_string("test@localhost/resource")
assert jid(jid, :user) == "test"
assert jid(jid, :server) == "localhost"
assert jid(jid, :resource) == "resource"
end
test "Check that sending a list to from_string/1 does not crash the jid process" do
{:error, :need_jid_as_binary} = :jid.from_string('test@localhost/resource')
end
end

View File

@ -1,374 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule EjabberdModAdminExtraTest do
use ExUnit.Case, async: false
require EjabberdAuthMock
require EjabberdSmMock
require ModLastMock
require ModRosterMock
@author "jsautret@process-one.net"
@user "user"
@domain "domain"
@password "password"
@resource "resource"
require Record
Record.defrecord :jid, Record.extract(:jid, from_lib: "xmpp/include/jid.hrl")
setup_all do
try do
:jid.start
:stringprep.start
:mnesia.start
:ejabberd_mnesia.start
:p1_sha.load_nif
:ejabberd_hooks.start_link
rescue
_ -> :ok
end
:ok = :ejabberd_config.start(["domain"], [])
:gen_mod.start_link
:acl.start_link
:ejabberd_access_permissions.start_link()
:ejabberd_commands.start_link
:mod_admin_extra.start(@domain, [])
:ejabberd_hooks.start_link
:ok
end
setup do
:meck.unload
EjabberdAuthMock.init
EjabberdSmMock.init
ModRosterMock.init(@domain, :mod_admin_extra)
:ok
end
###################### Accounts
test "check_account works" do
EjabberdAuthMock.create_user @user, @domain, @password
assert call_command(:check_account, [@user, @domain])
refute call_command(:check_account, [@user, "bad_domain"])
refute call_command(:check_account, ["bad_user", @domain])
assert :meck.validate :ejabberd_auth
end
test "check_password works" do
EjabberdAuthMock.create_user @user, @domain, @password
assert call_command(:check_password,
[@user, @domain, @password])
refute call_command(:check_password,
[@user, @domain, "bad_password"])
refute call_command(:check_password,
[@user, "bad_domain", @password])
refute call_command(:check_password,
["bad_user", @domain, @password])
assert :meck.validate :ejabberd_auth
end
test "check_password_hash works" do
EjabberdAuthMock.create_user @user, @domain, @password
hash = "5F4DCC3B5AA765D61D8327DEB882CF99" # echo -n password|md5
assert call_command(:check_password_hash,
[@user, @domain, hash, "md5"])
refute call_command(:check_password_hash,
[@user, @domain, "bad_hash", "md5"])
refute call_command(:check_password_hash,
[@user, "bad_domain", hash, "md5"])
refute call_command(:check_password_hash,
["bad_user", @domain, hash, "md5"])
hash = "5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8" # echo -n password|shasum
assert call_command(:check_password_hash,
[@user, @domain, hash, "sha"])
assert :unkown_hash_method ==
catch_throw call_command(:check_password_hash,
[@user, @domain, hash, "bad_method"])
assert :meck.validate :ejabberd_auth
end
test "set_password works" do
EjabberdAuthMock.create_user @user, @domain, @password
assert call_command(:change_password,
[@user, @domain, "new_password"])
refute call_command(:check_password,
[@user, @domain, @password])
assert call_command(:check_password,
[@user, @domain, "new_password"])
assert {:not_found, 'unknown_user'} ==
catch_throw call_command(:change_password,
["bad_user", @domain,
@password])
assert :meck.validate :ejabberd_auth
end
###################### Sessions
test "num_resources works" do
assert 0 == call_command(:num_resources,
[@user, @domain])
EjabberdSmMock.connect_resource @user, @domain, @resource
assert 1 == call_command(:num_resources,
[@user, @domain])
EjabberdSmMock.connect_resource @user, @domain, @resource<>"2"
assert 2 == call_command(:num_resources,
[@user, @domain])
EjabberdSmMock.connect_resource @user<>"1", @domain, @resource
assert 2 == call_command(:num_resources,
[@user, @domain])
EjabberdSmMock.disconnect_resource @user, @domain, @resource
assert 1 == call_command(:num_resources,
[@user, @domain])
assert :meck.validate :ejabberd_sm
end
test "resource_num works" do
EjabberdSmMock.connect_resource @user, @domain, @resource<>"3"
EjabberdSmMock.connect_resource @user, @domain, @resource<>"2"
EjabberdSmMock.connect_resource @user, @domain, @resource<>"1"
assert :bad_argument ==
elem(catch_throw(call_command(:resource_num,
[@user, @domain, 0])), 0)
assert @resource<>"1" ==
call_command(:resource_num, [@user, @domain, 1])
assert @resource<>"3" ==
call_command(:resource_num, [@user, @domain, 3])
assert :bad_argument ==
elem(catch_throw(call_command(:resource_num,
[@user, @domain, 4])), 0)
assert :meck.validate :ejabberd_sm
end
test "kick_session works" do
EjabberdSmMock.connect_resource @user, @domain, @resource<>"1"
EjabberdSmMock.connect_resource @user, @domain, @resource<>"2"
EjabberdSmMock.connect_resource @user, @domain, @resource<>"3"
assert 3 == length EjabberdSmMock.get_sessions @user, @domain
assert 1 == length EjabberdSmMock.get_session @user, @domain, @resource<>"2"
assert :ok ==
call_command(:kick_session,
[@user, @domain,
@resource<>"2", "kick"])
assert 2 == length EjabberdSmMock.get_sessions @user, @domain
assert 0 == length EjabberdSmMock.get_session @user, @domain, @resource<>"2"
assert :meck.validate :ejabberd_sm
end
###################### Last
test "get_last works" do
assert {_, 'NOT FOUND'} =
call_command(:get_last, [@user, @domain])
EjabberdSmMock.connect_resource @user, @domain, @resource<>"1"
EjabberdSmMock.connect_resource @user, @domain, @resource<>"2"
assert {_, 'ONLINE'} =
call_command(:get_last, [@user, @domain])
EjabberdSmMock.disconnect_resource @user, @domain, @resource<>"1"
assert {_, 'ONLINE'} =
call_command(:get_last, [@user, @domain])
now = {megasecs, secs, _microsecs} = :os.timestamp
timestamp = megasecs * 1000000 + secs
EjabberdSmMock.disconnect_resource(@user, @domain, @resource<>"2",
timestamp)
{{year, month, day}, {hour, minute, second}} = :calendar.now_to_universal_time now
result = IO.iodata_to_binary(:io_lib.format(
"~w-~.2.0w-~.2.0wT~.2.0w:~.2.0w:~.2.0wZ",
[year, month, day, hour, minute, second]))
assert {result, ""} ==
call_command(:get_last, [@user, @domain])
assert :meck.validate :mod_last
end
###################### Roster
@tag :skip
test "add_rosteritem and delete_rosteritem work" do
# Connect user
# Add user1 & user2 to user's roster
# Remove user1 & user2 from user's roster
EjabberdSmMock.connect_resource @user, @domain, @resource
assert [] == ModRosterMock.get_roster(@user, @domain)
assert :ok ==
call_command(:add_rosteritem, [@user, @domain,
@user<>"1", @domain,
"nick1",
"group1",
"both"])
# Check that user1 is the only item of the user's roster
result = ModRosterMock.get_roster(@user, @domain)
assert 1 == length result
[{{@user, @domain, jid}, opts}] = result
assert @user<>"1@"<>@domain == jid
assert "nick1" == opts.nick
assert ["group1"] == opts.groups
assert :both == opts.subs
# Check that the item roster user1 was pushed with subscription
# 'both' to user online ressource
jid = :jlib.make_jid(@user, @domain, @resource)
assert 1 ==
:meck.num_calls(:ejabberd_sm, :route,
[jid,
{:item, {@user<>"1", @domain, ""}, :both}])
assert :ok ==
call_command(:add_rosteritem, [@user, @domain,
@user<>"2", @domain,
"nick2",
"group2",
"both"])
result = ModRosterMock.get_roster(@user, @domain)
assert 2 == length result
# Check that the item roster user2 was pushed with subscription
# 'both' to user online ressource
assert 1 ==
:meck.num_calls(:ejabberd_sm, :route,
[jid,
{:item, {@user<>"2", @domain, ""}, :both}])
call_command(:delete_rosteritem, [@user, @domain,
@user<>"1", @domain])
result = ModRosterMock.get_roster(@user, @domain)
assert 1 == length result
[{{@user, @domain, jid}, opts}] = result
assert @user<>"2@"<>@domain == jid
assert "nick2" == opts.nick
assert ["group2"] == opts.groups
assert :both == opts.subs
# Check that the item roster user1 was pushed with subscription
# 'none' to user online ressource
jid = :jlib.make_jid(@user, @domain, @resource)
assert 1 ==
:meck.num_calls(:ejabberd_sm, :route,
[jid,
{:item, {@user<>"1", @domain, ""}, :none}])
call_command(:delete_rosteritem, [@user, @domain,
@user<>"2", @domain])
# Check that the item roster user2 was pushed with subscription
# 'none' to user online ressource
assert 1 ==
:meck.num_calls(:ejabberd_sm, :route,
[jid,
{:item, {@user<>"2", @domain, ""}, :none}])
# Check that nothing else was pushed to user resource
jid = jid(user: @user, server: @domain, resource: :_,
luser: @user, lserver: @domain, lresource: :_)
assert 4 ==
:meck.num_calls(:ejabberd_sm, :route,
[jid,
{:item, :_, :_}])
assert [] == ModRosterMock.get_roster(@user, @domain)
assert :meck.validate :ejabberd_sm
end
@tag :skip
test "get_roster works" do
assert [] == ModRosterMock.get_roster(@user, @domain)
assert [] == call_command(:get_roster, [@user, @domain],
:admin)
assert :ok ==
call_command(:add_rosteritem, [@user, @domain,
@user<>"1", @domain,
"nick1",
"group1",
"both"])
assert [{@user<>"1@"<>@domain, "", 'both', 'none', "group1"}] ==
call_command(:get_roster, [@user, @domain], :admin)
assert :ok ==
call_command(:add_rosteritem, [@user, @domain,
@user<>"2", @domain,
"nick2",
"group2",
"none"])
result = call_command(:get_roster, [@user, @domain], :admin)
assert 2 == length result
assert Enum.member?(result, {@user<>"1@"<>@domain, "", 'both', 'none', "group1"})
assert Enum.member?(result, {@user<>"2@"<>@domain, "", 'none', 'none', "group2"})
end
defp call_command(name, args) do
:ejabberd_commands.execute_command2(name, args, %{:caller_module => :ejabberd_ctl})
end
defp call_command(name, args, mode) do
call_command(name, args)
end
# kick_user command is defined in ejabberd_sm, move to extra?
# test "kick_user works" do
# assert 0 == call_command(:num_resources,
# [@user, @domain])
# EjabberdSmMock.connect_resource(@user, @domain, @resource<>"1")
# EjabberdSmMock.connect_resource(@user, @domain, @resource<>"2")
# assert 2 ==
# call_command(:kick_user, [@user, @domain])
# assert 0 == call_command(:num_resources,
# [@user, @domain])
# assert :meck.validate :ejabberd_sm
# end
end

View File

@ -1,270 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule ModHttpApiMockTest do
use ExUnit.Case, async: false
@author "jsautret@process-one.net"
# Admin user
@admin "admin"
@adminpass "adminpass"
# Non admin user
@user "user"
@userpass "userpass"
# XMPP domain
@domain "domain"
# mocked command
@command "command_test"
@acommand String.to_atom(@command)
# default API version
@version 0
require Record
Record.defrecord :request, Record.extract(:request, from_lib: "ejabberd/include/ejabberd_http.hrl")
setup_all do
try do
:jid.start
:mnesia.start
:ejabberd_mnesia.start
:stringprep.start
:ejabberd_hooks.start_link
:ejabberd_config.start([@domain], [])
{:ok, _} = :ejabberd_access_permissions.start_link()
:ejabberd_commands.start_link
rescue
_ -> :ok
end
:mod_http_api.start(@domain, [])
EjabberdOauthMock.init
:ok
end
setup do
:meck.unload
:meck.new :ejabberd_commands
:meck.new(:acl, [:passthrough]) # Need to fake acl to allow oauth
EjabberdAuthMock.init
:ok
end
test "HTTP GET simple command call with Basic Auth" do
EjabberdAuthMock.create_user @user, @domain, @userpass
# Mock a simple command() -> :ok
:meck.expect(:ejabberd_commands, :get_command_format,
fn (@acommand, %{usr: {@user, @domain, _}}, @version) ->
{[], {:res, :rescode}}
end)
:meck.expect(:ejabberd_commands, :get_exposed_commands,
fn () -> [@acommand] end)
:meck.expect(:ejabberd_commands, :execute_command2,
fn (@acommand, [], %{usr: {@user, @domain, _}}, @version) ->
:ok
end)
:ejabberd_config.add_local_option(:commands, [[{:add_commands, [@acommand]}]])
# Correct Basic Auth call
req = request(method: :GET,
path: ["api", @command],
q: [nokey: ""],
# Basic auth
auth: {@user<>"@"<>@domain, @userpass},
ip: {{127,0,0,1},60000},
host: @domain)
result = :mod_http_api.process([@command], req)
# history = :meck.history(:ejabberd_commands)
assert 200 == elem(result, 0) # HTTP code
assert "0" == elem(result, 2) # command result
# Bad password
req = request(method: :GET,
path: ["api", @command],
q: [nokey: ""],
# Basic auth
auth: {@user<>"@"<>@domain, @userpass<>"bad"},
ip: {{127,0,0,1},60000},
host: @domain)
result = :mod_http_api.process([@command], req)
assert 401 == elem(result, 0) # HTTP code
# Check that the command was executed only once
assert 1 ==
:meck.num_calls(:ejabberd_commands, :execute_command2, :_)
assert :meck.validate :ejabberd_auth
assert :meck.validate :ejabberd_commands
end
test "HTTP GET simple command call with OAuth" do
EjabberdAuthMock.create_user @user, @domain, @userpass
# Mock a simple command() -> :ok
:meck.expect(:ejabberd_commands, :get_command_format,
fn (@acommand, %{usr: {@user, @domain, _}}, @version) ->
{[], {:res, :rescode}}
end)
:meck.expect(:ejabberd_commands, :get_exposed_commands,
fn () -> [@acommand] end)
:meck.expect(:ejabberd_commands, :execute_command2,
fn (@acommand, [], %{usr: {@user, @domain, _}, oauth_scope: ["ejabberd:user"]}, @version) ->
:ok
(@acommand, [], %{usr: {@user, @domain, _}, oauth_scope: [@command]}, @version) ->
:ok
(@acommand, [], %{usr: {@user, @domain, _}, oauth_scope: _}, @version) ->
throw({:error, :access_rules_unauthorized})
end)
# Correct OAuth call using specific scope
token = EjabberdOauthMock.get_token @user, @domain, @command
req = request(method: :GET,
path: ["api", @command],
q: [nokey: ""],
# OAuth
auth: {:oauth, token, []},
ip: {{127,0,0,1},60000},
host: @domain)
result = :mod_http_api.process([@command], req)
assert 200 == elem(result, 0) # HTTP code
assert "0" == elem(result, 2) # command result
# Correct OAuth call using specific ejabberd:user scope
token = EjabberdOauthMock.get_token @user, @domain, "ejabberd:user"
req = request(method: :GET,
path: ["api", @command],
q: [nokey: ""],
# OAuth
auth: {:oauth, token, []},
ip: {{127,0,0,1},60000},
host: @domain)
result = :mod_http_api.process([@command], req)
assert 200 == elem(result, 0) # HTTP code
assert "0" == elem(result, 2) # command result
# Wrong OAuth token
req = request(method: :GET,
path: ["api", @command],
q: [nokey: ""],
# OAuth
auth: {:oauth, "bad"<>token, []},
ip: {{127,0,0,1},60000},
host: @domain)
result = :mod_http_api.process([@command], req)
assert 401 == elem(result, 0) # HTTP code
# Expired OAuth token
token = EjabberdOauthMock.get_token @user, @domain, @command, 1
:timer.sleep 1500
req = request(method: :GET,
path: ["api", @command],
q: [nokey: ""],
# OAuth
auth: {:oauth, token, []},
ip: {{127,0,0,1},60000},
host: @domain)
result = :mod_http_api.process([@command], req)
assert 401 == elem(result, 0) # HTTP code
# Wrong OAuth scope
token = EjabberdOauthMock.get_token @user, @domain, "bad_command"
:timer.sleep 1500
req = request(method: :GET,
path: ["api", @command],
q: [nokey: ""],
# OAuth
auth: {:oauth, token, []},
ip: {{127,0,0,1},60000},
host: @domain)
result = :mod_http_api.process([@command], req)
assert 403 == elem(result, 0) # HTTP code
# Check that the command was executed twice
assert 3 ==
:meck.num_calls(:ejabberd_commands, :execute_command2, :_)
assert :meck.validate :ejabberd_auth
#assert :meck.validate :ejabberd_commands
#assert :ok = :meck.history(:ejabberd_commands)
end
test "Request oauth token, resource owner password credentials" do
EjabberdAuthMock.create_user @user, @domain, @userpass
:application.set_env(:oauth2, :backend, :ejabberd_oauth)
:application.start(:oauth2)
# Mock a simple command() -> :ok
:meck.expect(:ejabberd_commands, :get_command_format,
fn (@acommand, {@user, @domain, {:oauth, _token}, false}, @version) ->
{[], {:res, :rescode}}
end)
:meck.expect(:ejabberd_commands, :get_exposed_commands,
fn () -> [@acommand] end)
#Mock acl to allow oauth authorizations
:meck.expect(:acl, :match_rule, fn(_Server, _Access, _Jid) -> :allow end)
# Correct password
req = request(method: :POST,
path: ["oauth", "token"],
q: [{"grant_type", "password"}, {"scope", @command}, {"username", @user<>"@"<>@domain}, {"ttl", "4000"}, {"password", @userpass}],
ip: {{127,0,0,1},60000},
host: @domain)
result = :ejabberd_oauth.process([], req)
assert 200 = elem(result, 0) #http code
{kv} = :jiffy.decode(elem(result,2))
assert {_, "bearer"} = List.keyfind(kv, "token_type", 0)
assert {_, @command} = List.keyfind(kv, "scope", 0)
assert {_, 4000} = List.keyfind(kv, "expires_in", 0)
{"access_token", _token} = List.keyfind(kv, "access_token", 0)
#missing grant_type
req = request(method: :POST,
path: ["oauth", "token"],
q: [{"scope", @command}, {"username", @user<>"@"<>@domain}, {"password", @userpass}],
ip: {{127,0,0,1},60000},
host: @domain)
result = :ejabberd_oauth.process([], req)
assert 400 = elem(result, 0) #http code
{kv} = :jiffy.decode(elem(result,2))
assert {_, "unsupported_grant_type"} = List.keyfind(kv, "error", 0)
# incorrect user/pass
req = request(method: :POST,
path: ["oauth", "token"],
q: [{"grant_type", "password"}, {"scope", @command}, {"username", @user<>"@"<>@domain}, {"password", @userpass<>"aa"}],
ip: {{127,0,0,1},60000},
host: @domain)
result = :ejabberd_oauth.process([], req)
assert 400 = elem(result, 0) #http code
{kv} = :jiffy.decode(elem(result,2))
assert {_, "invalid_grant"} = List.keyfind(kv, "error", 0)
assert :meck.validate :ejabberd_auth
assert :meck.validate :ejabberd_commands
end
end

View File

@ -1,127 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule ModHttpApiTest do
@author "mremond@process-one.net"
use ExUnit.Case, async: true
require Record
Record.defrecord :request, Record.extract(:request, from_lib: "ejabberd/include/ejabberd_http.hrl")
Record.defrecord :ejabberd_commands, Record.extract(:ejabberd_commands, from_lib: "ejabberd/include/ejabberd_commands.hrl")
setup_all do
:ok = :mnesia.start
:ejabberd_mnesia.start
:stringprep.start
:ejabberd_hooks.start_link
:ok = :ejabberd_config.start(["localhost"], [])
:acl.start_link
{:ok, _} = :ejabberd_access_permissions.start_link()
{:ok, _} = :ejabberd_commands.start_link
:ok = :ejabberd_commands.register_commands(cmds)
on_exit fn ->
:meck.unload
unregister_commands(cmds) end
end
test "We can expose several commands to API at a time" do
setup_mocks()
assert :ok == :ejabberd_commands.expose_commands([:open_cmd, :user_cmd])
commands = :ejabberd_commands.get_exposed_commands()
assert Enum.member?(commands, :open_cmd)
assert Enum.member?(commands, :user_cmd)
end
# test "We can call open commands without authentication" do
# setup_mocks()
# :ejabberd_commands.expose_commands([:open_cmd])
# request = request(method: :POST, ip: {{127,0,0,1},50000}, data: "[]")
# {200, _, _} = :mod_http_api.process(["open_cmd"], request)
# end
# This related to the commands config file option
test "Attempting to access a command that is not exposed as HTTP API returns 403" do
setup_mocks()
assert :ok == :ejabberd_commands.expose_commands([])
request = request(method: :POST, ip: {{127,0,0,1},50000}, data: "{}")
{403, _, _} = :mod_http_api.process(["open_cmd"], request)
end
test "Call to user, admin or restricted commands without authentication are rejected" do
setup_mocks()
assert :ok == :ejabberd_commands.expose_commands([:user_cmd, :admin_cmd, :restricted])
request = request(method: :POST, ip: {{127,0,0,1},50000}, data: "{}")
{400, _, _} = :mod_http_api.process(["user_cmd"], request)
{403, _, _} = :mod_http_api.process(["admin_cmd"], request)
{403, _, _} = :mod_http_api.process(["restricted_cmd"], request)
end
@tag pending: true
test "If admin_ip_access is enabled, we can call restricted API without authentication from that IP" do
setup_mocks()
end
# Define a set of test commands that we expose through API
# We define one for each policy type
defp cmds do
[:open, :user, :admin, :restricted]
|> Enum.map(&({&1, String.to_atom(to_string(&1) <> "_cmd")}))
|> Enum.map(fn({cmd_type, cmd}) ->
ejabberd_commands(name: cmd, tags: [:test],
policy: cmd_type,
module: __MODULE__,
function: cmd,
args: [],
result: {:res, :rescode})
end)
end
def open_cmd, do: :ok
def user_cmd(_, _), do: :ok
def admin_cmd, do: :ok
def restricted_cmd, do: :ok
defp setup_mocks() do
:meck.unload
mock(:gen_mod, :get_module_opt,
fn (_server, :mod_http_api, _admin_ip_access, _, _) ->
[{:allow, [{:ip, {{127,0,0,2}, 32}}]}]
end)
end
defp mock(module, function, fun) do
try do
:meck.new(module)
catch
:error, {:already_started, _pid} -> :ok
end
:meck.expect(module, function, fun)
end
defp unregister_commands(commands) do
try do
:ejabberd_commands.unregister_commands(commands)
catch
_,_ -> :ok
end
end
end

View File

@ -1,79 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule ModLastMock do
require Record
Record.defrecord :session, Record.extract(:session, from_lib: "ejabberd/include/ejabberd_sm.hrl")
Record.defrecord :jid, Record.extract(:jid, from_lib: "xmpp/include/jid.hrl")
@author "jsautret@process-one.net"
@agent __MODULE__
def init do
try do
Agent.stop(@agent)
catch
:exit, _e -> :ok
end
{:ok, _pid} = Agent.start_link(fn -> %{} end, name: @agent)
mock(:mod_last, :get_last_info,
fn (user, domain) ->
Agent.get(@agent, fn last ->
case Map.get(last, {user, domain}, :not_found) do
{ts, status} -> {:ok, ts, status}
result -> result
end
end)
end)
end
def set_last(user, domain, status) do
set_last(user, domain, status, now)
end
def set_last(user, domain, status, timestamp) do
Agent.update(@agent, fn last ->
Map.put(last, {user, domain}, {timestamp, status})
end)
end
####################################################################
# Helpers
####################################################################
def now() do
{megasecs, secs, _microsecs} = :os.timestamp
megasecs * 1000000 + secs
end
# TODO refactor: Move to ejabberd_test_mock
def mock(module, function, fun) do
try do
:meck.new(module)
catch
:error, {:already_started, _pid} -> :ok
end
:meck.expect(module, function, fun)
end
end

View File

@ -1,159 +0,0 @@
# ----------------------------------------------------------------------
#
# ejabberd, Copyright (C) 2002-2017 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ----------------------------------------------------------------------
defmodule ModRosterMock do
@author "jsautret@process-one.net"
require Record
Record.defrecord :roster, Record.extract(:roster, from_lib: "ejabberd/include/mod_roster.hrl")
Record.defrecord :roster_version, Record.extract(:roster_version, from_lib: "ejabberd/include/mod_roster.hrl")
@agent __MODULE__
def init(domain, module) do
try do
Agent.stop(@agent)
catch
:exit, _e -> :ok
end
{:ok, _pid} = Agent.start_link(fn -> %{} end, name: @agent)
mock_with_meck
:ejabberd_mnesia.create(:mod_roster_mnesia, :roster,
[ram_copies: [node()],
attributes: Keyword.keys(roster(roster())),
index: [:us]])
:ejabberd_mnesia.create(:mod_roster_mnesia, :roster_version,
[ram_copies: [node()],
attributes: Keyword.keys(roster_version(roster_version()))])
#:mod_roster.stop(domain)
:gen_mod.start_module(domain, :mod_roster)
end
def mock_with_meck do
# mock(:gen_mod, :db_type,
# fn (_server, :mod_roster_mnesia) ->
# :mnesia
# end)
#
# mock(:mnesia, :transaction,
# fn (_server, function) ->
# {:atomic, function.()}
# end)
#
# mock(:mnesia, :write,
# fn (Item) ->
# throw Item
# {:atomic, :ok}
# end)
mock(:mod_roster_mnesia, :init,
fn (_server, _opts) ->
:ok
end)
mock(:mod_roster_mnesia, :transaction,
fn (_server, function) ->
{:atomic, function.()}
end)
mock(:mod_roster_mnesia, :update_roster_t,
fn (user, domain, {u, d, _r}, item) ->
add_roster_item(user, domain, u<>"@"<>d,
roster(item, :name),
roster(item, :subscription),
roster(item, :groups),
roster(item, :ask),
roster(item, :askmessage))
end)
mock(:mod_roster_mnesia, :invalidate_roster_cache,
fn (_user, _server) ->
:ok
end)
end
def add_roster_item(user, domain, jid, nick, subs \\ :none, groups \\ [],
ask \\ :none, askmessage \\ "")
when is_binary(user) and byte_size(user) > 0
and is_binary(domain) and byte_size(domain) > 0
and is_binary(jid) and byte_size(jid) > 0
and is_binary(nick)
and is_atom(subs)
and is_list(groups)
and is_atom(ask)
and is_binary(askmessage)
do
Agent.update(@agent, fn roster ->
Map.put(roster, {user, domain, jid}, %{nick: nick,
subs: subs, groups: groups,
ask: ask, askmessage: askmessage})
end)
end
def remove_roster_item(user, domain, jid) do
Agent.update(@agent, fn roster ->
Map.delete(roster, {user, domain, jid})
end)
end
def get_rosters() do
Agent.get(@agent, fn roster -> roster end)
end
def get_roster(user, domain) do
Agent.get(@agent, fn roster ->
for {u, d, jid} <- Map.keys(roster), u == user, d == domain,
do: {{u, d, jid}, Map.fetch!(roster, {u, d, jid})}
end)
end
def to_record({{user, domain, jid}, r}) do
roster(usj: {user, domain, jid},
us: {user, domain},
jid: :jid.from_string(jid),
subscription: r.subs,
ask: r.ask,
groups: r.groups,
askmessage: r.askmessage
)
end
def to_records(rosters) do
for item <- rosters, do: to_record(item)
end
####################################################################
# Helpers
####################################################################
# TODO refactor: Move to ejabberd_test_mock
def mock(module, function, fun) do
try do
:meck.new(module, [:non_strict, :passthrough, :unstick])
catch
:error, {:already_started, _pid} -> :ok
end
:meck.expect(module, function, fun)
end
end

View File

@ -1,7 +0,0 @@
Code.require_file "ejabberd_auth_mock.exs", __DIR__
Code.require_file "ejabberd_oauth_mock.exs", __DIR__
Code.require_file "ejabberd_sm_mock.exs", __DIR__
Code.require_file "mod_last_mock.exs", __DIR__
Code.require_file "mod_roster_mock.exs", __DIR__
ExUnit.start