aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/ejabberd_SUITE.erl730
-rw-r--r--test/ejabberd_SUITE_data/ejabberd.yml88
-rw-r--r--test/ejabberd_hooks_test.exs53
-rw-r--r--test/elixir_SUITE.erl27
-rw-r--r--test/jid_test.exs44
-rw-r--r--test/ldap_srv.erl2
-rw-r--r--test/suite.erl52
-rw-r--r--test/suite.hrl3
8 files changed, 871 insertions, 128 deletions
diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl
index a827a9174..b9ba9956a 100644
--- a/test/ejabberd_SUITE.erl
+++ b/test/ejabberd_SUITE.erl
@@ -1,6 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2002-2015, ProcessOne
+%%% @copyright (C) 2002-2016, ProcessOne
%%% @doc
%%%
%%% @end
@@ -12,8 +12,8 @@
-import(suite, [init_config/1, connect/1, disconnect/1,
recv/0, send/2, send_recv/2, my_jid/1, server_jid/1,
- pubsub_jid/1, proxy_jid/1, muc_jid/1,
- muc_room_jid/1, get_features/2, re_register/1,
+ pubsub_jid/1, proxy_jid/1, muc_jid/1, muc_room_jid/1,
+ mix_jid/1, mix_room_jid/1, get_features/2, re_register/1,
is_feature_advertised/2, subscribe_to_events/1,
is_feature_advertised/3, set_opt/3, auth_SASL/2,
wait_for_master/1, wait_for_slave/1,
@@ -35,19 +35,57 @@ init_per_suite(Config) ->
LDIFFile = filename:join([DataDir, "ejabberd.ldif"]),
{ok, _} = file:copy(ExtAuthScript, filename:join([CWD, "extauth.py"])),
{ok, _} = ldap_srv:start(LDIFFile),
- ok = application:start(ejabberd),
+ start_ejabberd(NewConfig),
NewConfig.
+start_ejabberd(Config) ->
+ case proplists:get_value(backends, Config) of
+ all ->
+ ok = application:start(ejabberd, transient);
+ Backends when is_list(Backends) ->
+ Hosts = lists:map(fun(Backend) -> Backend ++ ".localhost" end, Backends),
+ application:load(ejabberd),
+ AllHosts = Hosts ++ ["localhost"], %% We always need localhost for the generic no_db tests
+ application:set_env(ejabberd, hosts, AllHosts),
+ ok = application:start(ejabberd, transient)
+ end.
+
end_per_suite(_Config) ->
- ok.
+ application:stop(ejabberd).
-init_per_group(no_db, Config) ->
+-define(BACKENDS, [mnesia,redis,mysql,pgsql,sqlite,ldap,extauth,riak]).
+
+init_per_group(Group, Config) ->
+ case lists:member(Group, ?BACKENDS) of
+ false ->
+ %% Not a backend related group, do default init:
+ do_init_per_group(Group, Config);
+ true ->
+ case proplists:get_value(backends, Config) of
+ all ->
+ %% All backends enabled
+ do_init_per_group(Group, Config);
+ Backends ->
+ %% Skipped backends that were not explicitely enabled
+ case lists:member(atom_to_list(Group), Backends) of
+ true ->
+ do_init_per_group(Group, Config);
+ false ->
+ {skip, {disabled_backend, Group}}
+ end
+ end
+ end.
+
+do_init_per_group(no_db, Config) ->
re_register(Config),
Config;
-init_per_group(mnesia, Config) ->
+do_init_per_group(mnesia, Config) ->
mod_muc:shutdown_rooms(?MNESIA_VHOST),
set_opt(server, ?MNESIA_VHOST, Config);
-init_per_group(mysql, Config) ->
+do_init_per_group(redis, Config) ->
+ mod_muc:shutdown_rooms(?REDIS_VHOST),
+ set_opt(server, ?REDIS_VHOST, Config);
+do_init_per_group(mysql, Config) ->
case catch ejabberd_odbc:sql_query(?MYSQL_VHOST, [<<"select 1;">>]) of
{selected, _, _} ->
mod_muc:shutdown_rooms(?MYSQL_VHOST),
@@ -56,7 +94,7 @@ init_per_group(mysql, Config) ->
Err ->
{skip, {mysql_not_available, Err}}
end;
-init_per_group(pgsql, Config) ->
+do_init_per_group(pgsql, Config) ->
case catch ejabberd_odbc:sql_query(?PGSQL_VHOST, [<<"select 1;">>]) of
{selected, _, _} ->
mod_muc:shutdown_rooms(?PGSQL_VHOST),
@@ -65,7 +103,7 @@ init_per_group(pgsql, Config) ->
Err ->
{skip, {pgsql_not_available, Err}}
end;
-init_per_group(sqlite, Config) ->
+do_init_per_group(sqlite, Config) ->
case catch ejabberd_odbc:sql_query(?SQLITE_VHOST, [<<"select 1;">>]) of
{selected, _, _} ->
mod_muc:shutdown_rooms(?SQLITE_VHOST),
@@ -73,11 +111,11 @@ init_per_group(sqlite, Config) ->
Err ->
{skip, {sqlite_not_available, Err}}
end;
-init_per_group(ldap, Config) ->
+do_init_per_group(ldap, Config) ->
set_opt(server, ?LDAP_VHOST, Config);
-init_per_group(extauth, Config) ->
+do_init_per_group(extauth, Config) ->
set_opt(server, ?EXTAUTH_VHOST, Config);
-init_per_group(riak, Config) ->
+do_init_per_group(riak, Config) ->
case ejabberd_riak:is_connected() of
true ->
mod_muc:shutdown_rooms(?RIAK_VHOST),
@@ -86,12 +124,14 @@ init_per_group(riak, Config) ->
Err ->
{skip, {riak_not_available, Err}}
end;
-init_per_group(_GroupName, Config) ->
+do_init_per_group(_GroupName, Config) ->
Pid = start_event_relay(),
set_opt(event_relay, Pid, Config).
end_per_group(mnesia, _Config) ->
ok;
+end_per_group(redis, _Config) ->
+ ok;
end_per_group(mysql, _Config) ->
ok;
end_per_group(pgsql, _Config) ->
@@ -131,14 +171,14 @@ init_per_testcase(TestCase, OrigConfig) ->
true -> Resource
end,
Slave = if IsCarbons ->
- jlib:make_jid(<<"test_master">>, Server, SlaveResource);
+ jid:make(<<"test_master">>, Server, SlaveResource);
true ->
- jlib:make_jid(<<"test_slave">>, Server, Resource)
+ jid:make(<<"test_slave">>, Server, Resource)
end,
Master = if IsCarbons ->
- jlib:make_jid(<<"test_master">>, Server, MasterResource);
+ jid:make(<<"test_master">>, Server, MasterResource);
true ->
- jlib:make_jid(<<"test_master">>, Server, Resource)
+ jid:make(<<"test_master">>, Server, Resource)
end,
Config = set_opt(user, User,
set_opt(slave, Slave,
@@ -214,6 +254,8 @@ db_tests(riak) ->
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
+ {test_flex_offline, [sequence],
+ [flex_offline_master, flex_offline_slave]},
{test_offline, [sequence],
[offline_master, offline_slave]},
{test_muc, [parallel],
@@ -225,7 +267,7 @@ db_tests(riak) ->
{test_roster_remove, [parallel],
[roster_remove_master,
roster_remove_slave]}];
-db_tests(mnesia) ->
+db_tests(DB) when DB == mnesia; DB == redis ->
[{single_user, [sequence],
[test_register,
auth_plain,
@@ -242,11 +284,19 @@ db_tests(mnesia) ->
test_unregister]},
{test_muc_register, [sequence],
[muc_register_master, muc_register_slave]},
+ {test_mix, [parallel],
+ [mix_master, mix_slave]},
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
+ {test_flex_offline, [sequence],
+ [flex_offline_master, flex_offline_slave]},
{test_offline, [sequence],
[offline_master, offline_slave]},
+ {test_old_mam, [parallel],
+ [mam_old_master, mam_old_slave]},
+ {test_new_mam, [parallel],
+ [mam_new_master, mam_new_slave]},
{test_carbons, [parallel],
[carbons_master, carbons_slave]},
{test_client_state, [parallel],
@@ -278,11 +328,19 @@ db_tests(_) ->
test_unregister]},
{test_muc_register, [sequence],
[muc_register_master, muc_register_slave]},
+ {test_mix, [parallel],
+ [mix_master, mix_slave]},
{test_roster_subscribe, [parallel],
[roster_subscribe_master,
roster_subscribe_slave]},
+ {test_flex_offline, [sequence],
+ [flex_offline_master, flex_offline_slave]},
{test_offline, [sequence],
[offline_master, offline_slave]},
+ {test_old_mam, [parallel],
+ [mam_old_master, mam_old_slave]},
+ {test_new_mam, [parallel],
+ [mam_new_master, mam_new_slave]},
{test_muc, [parallel],
[muc_master, muc_slave]},
{test_announce, [sequence],
@@ -308,6 +366,7 @@ groups() ->
{extauth, [sequence], extauth_tests()},
{no_db, [sequence], no_db_tests()},
{mnesia, [sequence], db_tests(mnesia)},
+ {redis, [sequence], db_tests(redis)},
{mysql, [sequence], db_tests(mysql)},
{pgsql, [sequence], db_tests(pgsql)},
{sqlite, [sequence], db_tests(sqlite)},
@@ -317,6 +376,7 @@ all() ->
[{group, ldap},
{group, no_db},
{group, mnesia},
+ {group, redis},
{group, mysql},
{group, pgsql},
{group, sqlite},
@@ -459,8 +519,8 @@ presence(Config) ->
presence_broadcast(Config) ->
Feature = <<"p1:tmp:", (randoms:get_string())/binary>>,
- Ver = crypto:sha(["client", $/, "bot", $/, "en", $/,
- "ejabberd_ct", $<, Feature, $<]),
+ Ver = crypto:hash(sha, ["client", $/, "bot", $/, "en", $/,
+ "ejabberd_ct", $<, Feature, $<]),
B64Ver = base64:encode(Ver),
Node = <<(?EJABBERD_CT_URI)/binary, $#, B64Ver/binary>>,
Server = ?config(server, Config),
@@ -541,7 +601,7 @@ disco(Config) ->
sm(Config) ->
Server = ?config(server, Config),
- ServerJID = jlib:make_jid(<<"">>, Server, <<"">>),
+ ServerJID = jid:make(<<"">>, Server, <<"">>),
Msg = #message{to = ServerJID, body = [#text{data = <<"body">>}]},
true = ?config(sm, Config),
%% Enable the session management with resumption enabled
@@ -563,7 +623,7 @@ sm_resume(Config) ->
{sm, SMConfig} = ?config(saved_config, Config),
ID = ?config(sm_previd, SMConfig),
Server = ?config(server, Config),
- ServerJID = jlib:make_jid(<<"">>, Server, <<"">>),
+ ServerJID = jid:make(<<"">>, Server, <<"">>),
MyJID = my_jid(Config),
Txt = #text{data = <<"body">>},
Msg = #message{from = ServerJID, to = MyJID, body = [Txt]},
@@ -579,7 +639,7 @@ sm_resume(Config) ->
private(Config) ->
Conference = #bookmark_conference{name = <<"Some name">>,
autojoin = true,
- jid = jlib:make_jid(
+ jid = jid:make(
<<"some">>,
<<"some.conference.org">>,
<<>>)},
@@ -667,7 +727,7 @@ privacy(Config) ->
blocking(Config) ->
true = is_feature_advertised(Config, ?NS_BLOCKING),
- JID = jlib:make_jid(<<"romeo">>, <<"montague.net">>, <<>>),
+ JID = jid:make(<<"romeo">>, <<"montague.net">>, <<>>),
#iq{type = result, sub_els = [#block_list{}]} =
send_recv(Config, #iq{type = get, sub_els = [#block_list{}]}),
I1 = send(Config, #iq{type = set,
@@ -831,15 +891,6 @@ pubsub(Config) ->
[#pubsub_affiliation{node = Node, type = owner}]}]} =
send_recv(Config, #iq{type = get, to = pubsub_jid(Config),
sub_els = [#pubsub{affiliations = []}]}),
- %% Get subscription options
- true = lists:member(?PUBSUB("subscription-options"), Features),
- #iq{type = result, sub_els = [#pubsub{options = #pubsub_options{
- node = Node}}]} =
- send_recv(Config,
- #iq{type = get, to = pubsub_jid(Config),
- sub_els = [#pubsub{options = #pubsub_options{
- node = Node,
- jid = my_jid(Config)}}]}),
%% Fetching published items from node "presence"
#iq{type = result,
sub_els = [#pubsub{items = #pubsub_items{
@@ -870,12 +921,96 @@ pubsub(Config) ->
jid = my_jid(Config)}}]}),
disconnect(Config).
+mix_master(Config) ->
+ MIX = mix_jid(Config),
+ Room = mix_room_jid(Config),
+ MyJID = my_jid(Config),
+ MyBareJID = jid:remove_resource(MyJID),
+ true = is_feature_advertised(Config, ?NS_MIX_0, MIX),
+ #iq{type = result,
+ sub_els =
+ [#disco_info{
+ identities = [#identity{category = <<"conference">>,
+ type = <<"text">>}],
+ xdata = [#xdata{type = result, fields = XFields}]}]} =
+ send_recv(Config, #iq{type = get, to = MIX, sub_els = [#disco_info{}]}),
+ true = lists:any(
+ fun(#xdata_field{var = <<"FORM_TYPE">>,
+ values = [?NS_MIX_SERVICEINFO_0]}) -> true;
+ (_) -> false
+ end, XFields),
+ %% Joining
+ Nodes = [?NS_MIX_NODES_MESSAGES, ?NS_MIX_NODES_PRESENCE,
+ ?NS_MIX_NODES_PARTICIPANTS, ?NS_MIX_NODES_SUBJECT,
+ ?NS_MIX_NODES_CONFIG],
+ I0 = send(Config, #iq{type = set, to = Room,
+ sub_els = [#mix_join{subscribe = Nodes}]}),
+ {_, #message{sub_els =
+ [#pubsub_event{
+ items = [#pubsub_event_items{
+ node = ?NS_MIX_NODES_PARTICIPANTS,
+ items = [#pubsub_event_item{
+ id = ParticipantID,
+ xml_els = [PXML]}]}]}]}} =
+ ?recv2(#iq{type = result, id = I0,
+ sub_els = [#mix_join{subscribe = Nodes, jid = MyBareJID}]},
+ #message{from = Room}),
+ #mix_participant{jid = MyBareJID} = xmpp_codec:decode(PXML),
+ %% Coming online
+ PresenceID = randoms:get_string(),
+ Presence = xmpp_codec:encode(#presence{}),
+ I1 = send(
+ Config,
+ #iq{type = set, to = Room,
+ sub_els =
+ [#pubsub{
+ publish = #pubsub_publish{
+ node = ?NS_MIX_NODES_PRESENCE,
+ items = [#pubsub_item{
+ id = PresenceID,
+ xml_els = [Presence]}]}}]}),
+ ?recv2(#iq{type = result, id = I1,
+ sub_els =
+ [#pubsub{
+ publish = #pubsub_publish{
+ node = ?NS_MIX_NODES_PRESENCE,
+ items = [#pubsub_item{id = PresenceID}]}}]},
+ #message{from = Room,
+ sub_els =
+ [#pubsub_event{
+ items = [#pubsub_event_items{
+ node = ?NS_MIX_NODES_PRESENCE,
+ items = [#pubsub_event_item{
+ id = PresenceID,
+ xml_els = [Presence]}]}]}]}),
+ %% Coming offline
+ send(Config, #presence{type = unavailable, to = Room}),
+ %% Receiving presence retract event
+ #message{from = Room,
+ sub_els = [#pubsub_event{
+ items = [#pubsub_event_items{
+ node = ?NS_MIX_NODES_PRESENCE,
+ retract = [PresenceID]}]}]} = recv(),
+ %% Leaving
+ I2 = send(Config, #iq{type = set, to = Room, sub_els = [#mix_leave{}]}),
+ ?recv2(#iq{type = result, id = I2, sub_els = []},
+ #message{from = Room,
+ sub_els =
+ [#pubsub_event{
+ items = [#pubsub_event_items{
+ node = ?NS_MIX_NODES_PARTICIPANTS,
+ retract = [ParticipantID]}]}]}),
+ disconnect(Config).
+
+mix_slave(Config) ->
+ disconnect(Config).
+
roster_subscribe_master(Config) ->
send(Config, #presence{}),
?recv1(#presence{}),
wait_for_slave(Config),
Peer = ?config(slave, Config),
- LPeer = jlib:jid_remove_resource(Peer),
+ LPeer = jid:remove_resource(Peer),
send(Config, #presence{type = subscribe, to = LPeer}),
Push1 = ?recv1(#iq{type = set,
sub_els = [#roster{items = [#roster_item{
@@ -927,7 +1062,7 @@ roster_subscribe_slave(Config) ->
?recv1(#presence{}),
wait_for_master(Config),
Peer = ?config(master, Config),
- LPeer = jlib:jid_remove_resource(Peer),
+ LPeer = jid:remove_resource(Peer),
?recv1(#presence{type = subscribe, from = LPeer}),
send(Config, #presence{type = subscribed, to = LPeer}),
Push1 = ?recv1(#iq{type = set,
@@ -956,7 +1091,7 @@ roster_subscribe_slave(Config) ->
roster_remove_master(Config) ->
MyJID = my_jid(Config),
Peer = ?config(slave, Config),
- LPeer = jlib:jid_remove_resource(Peer),
+ LPeer = jid:remove_resource(Peer),
Groups = [<<"A">>, <<"B">>],
wait_for_slave(Config),
send(Config, #presence{}),
@@ -990,7 +1125,7 @@ roster_remove_master(Config) ->
roster_remove_slave(Config) ->
MyJID = my_jid(Config),
Peer = ?config(master, Config),
- LPeer = jlib:jid_remove_resource(Peer),
+ LPeer = jid:remove_resource(Peer),
send(Config, #presence{}),
?recv1(#presence{from = MyJID, type = undefined}),
wait_for_master(Config),
@@ -1049,16 +1184,16 @@ proxy65_slave(Config) ->
muc_master(Config) ->
MyJID = my_jid(Config),
PeerJID = ?config(slave, Config),
- PeerBareJID = jlib:jid_remove_resource(PeerJID),
- PeerJIDStr = jlib:jid_to_string(PeerJID),
+ PeerBareJID = jid:remove_resource(PeerJID),
+ PeerJIDStr = jid:to_string(PeerJID),
MUC = muc_jid(Config),
Room = muc_room_jid(Config),
MyNick = ?config(master_nick, Config),
- MyNickJID = jlib:jid_replace_resource(Room, MyNick),
+ MyNickJID = jid:replace_resource(Room, MyNick),
PeerNick = ?config(slave_nick, Config),
- PeerNickJID = jlib:jid_replace_resource(Room, PeerNick),
+ PeerNickJID = jid:replace_resource(Room, PeerNick),
Subject = ?config(room_subject, Config),
- Localhost = jlib:make_jid(<<"">>, <<"localhost">>, <<"">>),
+ Localhost = jid:make(<<"">>, <<"localhost">>, <<"">>),
true = is_feature_advertised(Config, ?NS_MUC, MUC),
%% Joining
send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}),
@@ -1114,10 +1249,11 @@ muc_master(Config) ->
end
end, RoomCfg#xdata.fields),
NewRoomCfg = #xdata{type = submit, fields = NewFields},
- %% BUG: We should not receive any sub_els!
- #iq{type = result, sub_els = [_|_]} =
- send_recv(Config, #iq{type = set, to = Room,
- sub_els = [#muc_owner{config = NewRoomCfg}]}),
+ ID = send(Config, #iq{type = set, to = Room,
+ sub_els = [#muc_owner{config = NewRoomCfg}]}),
+ ?recv2(#iq{type = result, id = ID},
+ #message{from = Room, type = groupchat,
+ sub_els = [#muc_user{status_codes = [104]}]}),
%% Set subject
send(Config, #message{to = Room, type = groupchat,
body = [#text{data = Subject}]}),
@@ -1126,7 +1262,7 @@ muc_master(Config) ->
%% Sending messages (and thus, populating history for our peer)
lists:foreach(
fun(N) ->
- Text = #text{data = jlib:integer_to_binary(N)},
+ Text = #text{data = integer_to_binary(N)},
I = send(Config, #message{to = Room, body = [Text],
type = groupchat}),
?recv1(#message{from = MyNickJID, id = I,
@@ -1204,6 +1340,16 @@ muc_master(Config) ->
%% Receive groupchat message from the peer
?recv1(#message{type = groupchat, from = PeerNickJID,
body = [#text{data = Subject}]}),
+ %% Retrieving a member list
+ #iq{type = result, sub_els = [#muc_admin{items = MemberList}]} =
+ send_recv(Config,
+ #iq{type = get, to = Room,
+ sub_els =
+ [#muc_admin{items = [#muc_item{affiliation = member}]}]}),
+ [#muc_item{affiliation = member,
+ jid = Localhost},
+ #muc_item{affiliation = member,
+ jid = MyBareJID}] = lists:keysort(#muc_item.jid, MemberList),
%% Kick the peer
I2 = send(Config,
#iq{type = set, to = Room,
@@ -1238,16 +1384,16 @@ muc_master(Config) ->
muc_slave(Config) ->
MyJID = my_jid(Config),
- MyBareJID = jlib:jid_remove_resource(MyJID),
+ MyBareJID = jid:remove_resource(MyJID),
PeerJID = ?config(master, Config),
MUC = muc_jid(Config),
Room = muc_room_jid(Config),
MyNick = ?config(slave_nick, Config),
- MyNickJID = jlib:jid_replace_resource(Room, MyNick),
+ MyNickJID = jid:replace_resource(Room, MyNick),
PeerNick = ?config(master_nick, Config),
- PeerNickJID = jlib:jid_replace_resource(Room, PeerNick),
+ PeerNickJID = jid:replace_resource(Room, PeerNick),
Subject = ?config(room_subject, Config),
- Localhost = jlib:make_jid(<<"">>, <<"localhost">>, <<"">>),
+ Localhost = jid:make(<<"">>, <<"localhost">>, <<"">>),
%% Receive an invite from the peer
?recv1(#message{from = Room, type = normal,
sub_els =
@@ -1286,15 +1432,15 @@ muc_slave(Config) ->
%% Receive the room subject
?recv1(#message{from = PeerNickJID, type = groupchat,
body = [#text{data = Subject}],
- sub_els = [#delay{}, #legacy_delay{}]}),
+ sub_els = [#delay{}]}),
%% Receive MUC history
lists:foreach(
fun(N) ->
- Text = #text{data = jlib:integer_to_binary(N)},
+ Text = #text{data = integer_to_binary(N)},
?recv1(#message{from = PeerNickJID,
type = groupchat,
body = [Text],
- sub_els = [#delay{}, #legacy_delay{}]})
+ sub_els = [#delay{}]})
end, lists:seq(1, 5)),
%% Sending a voice request
VoiceReq = #xdata{
@@ -1323,16 +1469,6 @@ muc_slave(Config) ->
#muc_user{
items = [#muc_item{role = participant,
affiliation = member}]}]}),
- %% Retrieving a member list
- #iq{type = result, sub_els = [#muc_admin{items = MemberList}]} =
- send_recv(Config,
- #iq{type = get, to = Room,
- sub_els =
- [#muc_admin{items = [#muc_item{affiliation = member}]}]}),
- [#muc_item{affiliation = member,
- jid = Localhost},
- #muc_item{affiliation = member,
- jid = MyBareJID}] = lists:keysort(#muc_item.jid, MemberList),
%% Sending groupchat message
send(Config, #message{to = Room, type = groupchat,
body = [#text{data = Subject}]}),
@@ -1411,7 +1547,7 @@ muc_register_slave(Config) ->
announce_master(Config) ->
MyJID = my_jid(Config),
ServerJID = server_jid(Config),
- MotdJID = jlib:jid_replace_resource(ServerJID, <<"announce/motd">>),
+ MotdJID = jid:replace_resource(ServerJID, <<"announce/motd">>),
MotdText = #text{data = <<"motd">>},
send(Config, #presence{}),
?recv1(#presence{from = MyJID}),
@@ -1424,7 +1560,7 @@ announce_master(Config) ->
announce_slave(Config) ->
MyJID = my_jid(Config),
ServerJID = server_jid(Config),
- MotdDelJID = jlib:jid_replace_resource(ServerJID, <<"announce/motd/delete">>),
+ MotdDelJID = jid:replace_resource(ServerJID, <<"announce/motd/delete">>),
MotdText = #text{data = <<"motd">>},
send(Config, #presence{}),
?recv2(#presence{from = MyJID},
@@ -1433,9 +1569,138 @@ announce_slave(Config) ->
send(Config, #message{to = MotdDelJID}),
disconnect(Config).
+flex_offline_master(Config) ->
+ Peer = ?config(slave, Config),
+ LPeer = jid:remove_resource(Peer),
+ lists:foreach(
+ fun(I) ->
+ Body = integer_to_binary(I),
+ send(Config, #message{to = LPeer,
+ body = [#text{data = Body}],
+ subject = [#text{data = <<"subject">>}]})
+ end, lists:seq(1, 5)),
+ disconnect(Config).
+
+flex_offline_slave(Config) ->
+ MyJID = my_jid(Config),
+ MyBareJID = jid:remove_resource(MyJID),
+ Peer = ?config(master, Config),
+ Peer_s = jid:to_string(Peer),
+ true = is_feature_advertised(Config, ?NS_FLEX_OFFLINE),
+ %% Request disco#info
+ #iq{type = result,
+ sub_els = [#disco_info{
+ node = ?NS_FLEX_OFFLINE,
+ identities = Ids,
+ features = Fts,
+ xdata = [X]}]} =
+ send_recv(Config, #iq{type = get,
+ sub_els = [#disco_info{
+ node = ?NS_FLEX_OFFLINE}]}),
+ %% Check if we have correct identities
+ true = lists:any(
+ fun(#identity{category = <<"automation">>,
+ type = <<"message-list">>}) -> true;
+ (_) -> false
+ end, Ids),
+ %% Check if we have needed feature
+ true = lists:member(?NS_FLEX_OFFLINE, Fts),
+ %% Check xdata, the 'number_of_messages' should be 5
+ #xdata{type = result,
+ fields = [#xdata_field{type = hidden,
+ var = <<"FORM_TYPE">>},
+ #xdata_field{var = <<"number_of_messages">>,
+ values = [<<"5">>]}]} = X,
+ %% Fetch headers,
+ #iq{type = result,
+ sub_els = [#disco_items{
+ node = ?NS_FLEX_OFFLINE,
+ items = DiscoItems}]} =
+ send_recv(Config, #iq{type = get,
+ sub_els = [#disco_items{
+ node = ?NS_FLEX_OFFLINE}]}),
+ %% Check if headers are correct
+ Nodes = lists:sort(
+ lists:map(
+ fun(#disco_item{jid = J, name = P, node = N})
+ when (J == MyBareJID) and (P == Peer_s) ->
+ N
+ end, DiscoItems)),
+ %% Since headers are received we can send initial presence without a risk
+ %% of getting offline messages flood
+ send(Config, #presence{}),
+ ?recv1(#presence{from = MyJID}),
+ %% Check full fetch
+ I0 = send(Config, #iq{type = get, sub_els = [#offline{fetch = true}]}),
+ lists:foreach(
+ fun({I, N}) ->
+ Text = integer_to_binary(I),
+ ?recv1(#message{body = Body, sub_els = SubEls}),
+ [#text{data = Text}] = Body,
+ #offline{items = [#offline_item{node = N}]} =
+ lists:keyfind(offline, 1, SubEls),
+ #delay{} = lists:keyfind(delay, 1, SubEls)
+ end, lists:zip(lists:seq(1, 5), Nodes)),
+ ?recv1(#iq{type = result, id = I0, sub_els = []}),
+ %% Fetch 2nd and 4th message
+ I1 = send(Config,
+ #iq{type = get,
+ sub_els = [#offline{
+ items = [#offline_item{
+ action = view,
+ node = lists:nth(2, Nodes)},
+ #offline_item{
+ action = view,
+ node = lists:nth(4, Nodes)}]}]}),
+ lists:foreach(
+ fun({I, N}) ->
+ Text = integer_to_binary(I),
+ ?recv1(#message{body = [#text{data = Text}], sub_els = SubEls}),
+ #offline{items = [#offline_item{node = N}]} =
+ lists:keyfind(offline, 1, SubEls)
+ end, lists:zip([2, 4], [lists:nth(2, Nodes), lists:nth(4, Nodes)])),
+ ?recv1(#iq{type = result, id = I1, sub_els = []}),
+ %% Delete 2nd and 4th message
+ #iq{type = result, sub_els = []} =
+ send_recv(
+ Config,
+ #iq{type = set,
+ sub_els = [#offline{
+ items = [#offline_item{
+ action = remove,
+ node = lists:nth(2, Nodes)},
+ #offline_item{
+ action = remove,
+ node = lists:nth(4, Nodes)}]}]}),
+ %% Check if messages were deleted
+ #iq{type = result,
+ sub_els = [#disco_items{
+ node = ?NS_FLEX_OFFLINE,
+ items = RemainedItems}]} =
+ send_recv(Config, #iq{type = get,
+ sub_els = [#disco_items{
+ node = ?NS_FLEX_OFFLINE}]}),
+ RemainedNodes = [lists:nth(1, Nodes),
+ lists:nth(3, Nodes),
+ lists:nth(5, Nodes)],
+ RemainedNodes = lists:sort(
+ lists:map(
+ fun(#disco_item{node = N}) -> N end,
+ RemainedItems)),
+ %% Purge everything left
+ #iq{type = result, sub_els = []} =
+ send_recv(Config, #iq{type = set, sub_els = [#offline{purge = true}]}),
+ %% Check if there is no offline messages
+ #iq{type = result,
+ sub_els = [#disco_items{node = ?NS_FLEX_OFFLINE, items = []}]} =
+ send_recv(Config, #iq{type = get,
+ sub_els = [#disco_items{
+ node = ?NS_FLEX_OFFLINE}]}),
+ disconnect(Config).
+
offline_master(Config) ->
Peer = ?config(slave, Config),
- LPeer = jlib:jid_remove_resource(Peer),
+ LPeer = jid:remove_resource(Peer),
send(Config, #message{to = LPeer,
body = [#text{data = <<"body">>}],
subject = [#text{data = <<"subject">>}]}),
@@ -1450,12 +1715,11 @@ offline_slave(Config) ->
body = [#text{data = <<"body">>}],
subject = [#text{data = <<"subject">>}]}),
true = lists:keymember(delay, 1, SubEls),
- true = lists:keymember(legacy_delay, 1, SubEls),
disconnect(Config).
carbons_master(Config) ->
MyJID = my_jid(Config),
- MyBareJID = jlib:jid_remove_resource(MyJID),
+ MyBareJID = jid:remove_resource(MyJID),
Peer = ?config(slave, Config),
Txt = #text{data = <<"body">>},
true = is_feature_advertised(Config, ?NS_CARBONS_2),
@@ -1509,7 +1773,7 @@ carbons_master(Config) ->
carbons_slave(Config) ->
MyJID = my_jid(Config),
- MyBareJID = jlib:jid_remove_resource(MyJID),
+ MyBareJID = jid:remove_resource(MyJID),
Peer = ?config(master, Config),
Txt = #text{data = <<"body">>},
wait_for_master(Config),
@@ -1568,6 +1832,322 @@ carbons_slave(Config) ->
?recv1(#presence{from = Peer, type = unavailable}),
disconnect(Config).
+mam_old_master(Config) ->
+ mam_master(Config, ?NS_MAM_TMP).
+
+mam_new_master(Config) ->
+ mam_master(Config, ?NS_MAM_0).
+
+mam_master(Config, NS) ->
+ true = is_feature_advertised(Config, NS),
+ MyJID = my_jid(Config),
+ BareMyJID = jid:remove_resource(MyJID),
+ Peer = ?config(slave, Config),
+ send(Config, #presence{}),
+ ?recv1(#presence{}),
+ wait_for_slave(Config),
+ ?recv1(#presence{from = Peer}),
+ #iq{type = result, sub_els = [#mam_prefs{xmlns = NS, default = roster}]} =
+ send_recv(Config,
+ #iq{type = set,
+ sub_els = [#mam_prefs{xmlns = NS,
+ default = roster,
+ never = [MyJID]}]}),
+ if NS == ?NS_MAM_TMP ->
+ FakeArchived = #mam_archived{id = randoms:get_string(),
+ by = server_jid(Config)},
+ send(Config, #message{to = MyJID,
+ sub_els = [FakeArchived],
+ body = [#text{data = <<"a">>}]}),
+ send(Config, #message{to = BareMyJID,
+ sub_els = [FakeArchived],
+ body = [#text{data = <<"b">>}]}),
+ %% NOTE: The server should strip fake archived tags,
+ %% i.e. the sub_els received should be [].
+ ?recv2(#message{body = [#text{data = <<"a">>}], sub_els = []},
+ #message{body = [#text{data = <<"b">>}], sub_els = []});
+ true ->
+ ok
+ end,
+ wait_for_slave(Config),
+ lists:foreach(
+ fun(N) ->
+ Text = #text{data = integer_to_binary(N)},
+ send(Config,
+ #message{to = Peer, body = [Text]})
+ end, lists:seq(1, 5)),
+ ?recv1(#presence{type = unavailable, from = Peer}),
+ mam_query_all(Config, NS),
+ mam_query_with(Config, Peer, NS),
+ %% mam_query_with(Config, jid:remove_resource(Peer)),
+ mam_query_rsm(Config, NS),
+ #iq{type = result, sub_els = [#mam_prefs{xmlns = NS, default = never}]} =
+ send_recv(Config, #iq{type = set,
+ sub_els = [#mam_prefs{xmlns = NS,
+ default = never}]}),
+ disconnect(Config).
+
+mam_old_slave(Config) ->
+ mam_slave(Config, ?NS_MAM_TMP).
+
+mam_new_slave(Config) ->
+ mam_slave(Config, ?NS_MAM_0).
+
+mam_slave(Config, NS) ->
+ Peer = ?config(master, Config),
+ ServerJID = server_jid(Config),
+ wait_for_master(Config),
+ send(Config, #presence{}),
+ ?recv2(#presence{}, #presence{from = Peer}),
+ #iq{type = result, sub_els = [#mam_prefs{xmlns = NS, default = always}]} =
+ send_recv(Config,
+ #iq{type = set,
+ sub_els = [#mam_prefs{xmlns = NS, default = always}]}),
+ wait_for_master(Config),
+ lists:foreach(
+ fun(N) ->
+ Text = #text{data = integer_to_binary(N)},
+ ?recv1(#message{from = Peer, body = [Text],
+ sub_els = [#mam_archived{by = ServerJID}]})
+ end, lists:seq(1, 5)),
+ #iq{type = result, sub_els = [#mam_prefs{xmlns = NS, default = never}]} =
+ send_recv(Config, #iq{type = set,
+ sub_els = [#mam_prefs{xmlns = NS, default = never}]}),
+ disconnect(Config).
+
+mam_query_all(Config, NS) ->
+ QID = randoms:get_string(),
+ MyJID = my_jid(Config),
+ Peer = ?config(slave, Config),
+ Type = case NS of
+ ?NS_MAM_TMP -> get;
+ _ -> set
+ end,
+ I = send(Config, #iq{type = Type, sub_els = [#mam_query{xmlns = NS, id = QID}]}),
+ maybe_recv_iq_result(NS, I),
+ Iter = if NS == ?NS_MAM_TMP -> lists:seq(1, 5);
+ true -> lists:seq(1, 5) ++ lists:seq(1, 5)
+ end,
+ lists:foreach(
+ fun(N) ->
+ Text = #text{data = integer_to_binary(N)},
+ ?recv1(#message{to = MyJID,
+ sub_els =
+ [#mam_result{
+ queryid = QID,
+ sub_els =
+ [#forwarded{
+ delay = #delay{},
+ sub_els =
+ [#message{
+ from = MyJID, to = Peer,
+ body = [Text]}]}]}]})
+ end, Iter),
+ if NS == ?NS_MAM_TMP ->
+ ?recv1(#iq{type = result, id = I,
+ sub_els = [#mam_query{xmlns = NS, id = QID}]});
+ true ->
+ ?recv1(#message{sub_els = [#mam_fin{complete = true, id = QID}]})
+ end.
+
+mam_query_with(Config, JID, NS) ->
+ MyJID = my_jid(Config),
+ Peer = ?config(slave, Config),
+ {Query, Type} = if NS == ?NS_MAM_TMP ->
+ {#mam_query{xmlns = NS, with = JID}, get};
+ true ->
+ Fs = [#xdata_field{var = <<"jid">>,
+ values = [jid:to_string(JID)]}],
+ {#mam_query{xmlns = NS,
+ xdata = #xdata{type = submit, fields = Fs}}, set}
+ end,
+ I = send(Config, #iq{type = Type, sub_els = [Query]}),
+ Iter = if NS == ?NS_MAM_TMP -> lists:seq(1, 5);
+ true -> lists:seq(1, 5) ++ lists:seq(1, 5)
+ end,
+ maybe_recv_iq_result(NS, I),
+ lists:foreach(
+ fun(N) ->
+ Text = #text{data = integer_to_binary(N)},
+ ?recv1(#message{to = MyJID,
+ sub_els =
+ [#mam_result{
+ sub_els =
+ [#forwarded{
+ delay = #delay{},
+ sub_els =
+ [#message{
+ from = MyJID, to = Peer,
+ body = [Text]}]}]}]})
+ end, Iter),
+ if NS == ?NS_MAM_TMP ->
+ ?recv1(#iq{type = result, id = I,
+ sub_els = [#mam_query{xmlns = NS}]});
+ true ->
+ ?recv1(#message{sub_els = [#mam_fin{complete = true}]})
+ end.
+
+maybe_recv_iq_result(?NS_MAM_0, I1) ->
+ ?recv1(#iq{type = result, id = I1});
+maybe_recv_iq_result(_, _) ->
+ ok.
+
+mam_query_rsm(Config, NS) ->
+ MyJID = my_jid(Config),
+ Peer = ?config(slave, Config),
+ Type = case NS of
+ ?NS_MAM_TMP -> get;
+ _ -> set
+ end,
+ %% Get the first 3 items out of 5
+ I1 = send(Config,
+ #iq{type = Type,
+ sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{max = 3}}]}),
+ maybe_recv_iq_result(NS, I1),
+ lists:foreach(
+ fun(N) ->
+ Text = #text{data = integer_to_binary(N)},
+ ?recv1(#message{to = MyJID,
+ sub_els =
+ [#mam_result{
+ xmlns = NS,
+ sub_els =
+ [#forwarded{
+ delay = #delay{},
+ sub_els =
+ [#message{
+ from = MyJID, to = Peer,
+ body = [Text]}]}]}]})
+ end, lists:seq(1, 3)),
+ if NS == ?NS_MAM_TMP ->
+ ?recv1(#iq{type = result, id = I1,
+ sub_els = [#mam_query{xmlns = NS,
+ rsm = #rsm_set{last = Last, count = 5}}]});
+ true ->
+ ?recv1(#message{sub_els = [#mam_fin{
+ complete = false,
+ rsm = #rsm_set{last = Last, count = 10}}]})
+ end,
+ %% Get the next items starting from the `Last`.
+ %% Limit the response to 2 items.
+ I2 = send(Config,
+ #iq{type = Type,
+ sub_els = [#mam_query{xmlns = NS,
+ rsm = #rsm_set{max = 2,
+ 'after' = Last}}]}),
+ maybe_recv_iq_result(NS, I2),
+ lists:foreach(
+ fun(N) ->
+ Text = #text{data = integer_to_binary(N)},
+ ?recv1(#message{to = MyJID,
+ sub_els =
+ [#mam_result{
+ xmlns = NS,
+ sub_els =
+ [#forwarded{
+ delay = #delay{},
+ sub_els =
+ [#message{
+ from = MyJID, to = Peer,
+ body = [Text]}]}]}]})
+ end, lists:seq(4, 5)),
+ if NS == ?NS_MAM_TMP ->
+ ?recv1(#iq{type = result, id = I2,
+ sub_els = [#mam_query{
+ xmlns = NS,
+ rsm = #rsm_set{
+ count = 5,
+ first = #rsm_first{data = First}}}]});
+ true ->
+ ?recv1(#message{
+ sub_els = [#mam_fin{
+ complete = false,
+ rsm = #rsm_set{
+ count = 10,
+ first = #rsm_first{data = First}}}]})
+ end,
+ %% Paging back. Should receive 3 elements: 1, 2, 3.
+ I3 = send(Config,
+ #iq{type = Type,
+ sub_els = [#mam_query{xmlns = NS,
+ rsm = #rsm_set{max = 3,
+ before = First}}]}),
+ maybe_recv_iq_result(NS, I3),
+ lists:foreach(
+ fun(N) ->
+ Text = #text{data = integer_to_binary(N)},
+ ?recv1(#message{to = MyJID,
+ sub_els =
+ [#mam_result{
+ xmlns = NS,
+ sub_els =
+ [#forwarded{
+ delay = #delay{},
+ sub_els =
+ [#message{
+ from = MyJID, to = Peer,
+ body = [Text]}]}]}]})
+ end, lists:seq(1, 3)),
+ if NS == ?NS_MAM_TMP ->
+ ?recv1(#iq{type = result, id = I3,
+ sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{count = 5}}]});
+ true ->
+ ?recv1(#message{
+ sub_els = [#mam_fin{complete = true,
+ rsm = #rsm_set{count = 10}}]})
+ end,
+ %% Getting the item count. Should be 5 (or 10).
+ I4 = send(Config,
+ #iq{type = Type,
+ sub_els = [#mam_query{xmlns = NS,
+ rsm = #rsm_set{max = 0}}]}),
+ maybe_recv_iq_result(NS, I4),
+ if NS == ?NS_MAM_TMP ->
+ ?recv1(#iq{type = result, id = I4,
+ sub_els = [#mam_query{
+ xmlns = NS,
+ rsm = #rsm_set{count = 5,
+ first = undefined,
+ last = undefined}}]});
+ true ->
+ ?recv1(#message{
+ sub_els = [#mam_fin{
+ complete = false,
+ rsm = #rsm_set{count = 10,
+ first = undefined,
+ last = undefined}}]})
+ end,
+ %% Should receive 2 last messages
+ I5 = send(Config,
+ #iq{type = Type,
+ sub_els = [#mam_query{xmlns = NS,
+ rsm = #rsm_set{max = 2,
+ before = none}}]}),
+ maybe_recv_iq_result(NS, I5),
+ lists:foreach(
+ fun(N) ->
+ Text = #text{data = integer_to_binary(N)},
+ ?recv1(#message{to = MyJID,
+ sub_els =
+ [#mam_result{
+ xmlns = NS,
+ sub_els =
+ [#forwarded{
+ delay = #delay{},
+ sub_els =
+ [#message{
+ from = MyJID, to = Peer,
+ body = [Text]}]}]}]})
+ end, lists:seq(4, 5)),
+ if NS == ?NS_MAM_TMP ->
+ ?recv1(#iq{type = result, id = I5,
+ sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{count = 5}}]});
+ true ->
+ ?recv1(#message{
+ sub_els = [#mam_fin{complete = false,
+ rsm = #rsm_set{count = 10}}]})
+ end.
+
client_state_master(Config) ->
true = ?config(csi, Config),
Peer = ?config(slave, Config),
@@ -1596,7 +2176,7 @@ client_state_slave(Config) ->
change_client_state(Config, inactive),
wait_for_master(Config),
?recv1(#presence{from = Peer, type = unavailable,
- sub_els = [#delay{}, #legacy_delay{}]}),
+ sub_els = [#delay{}]}),
?recv1(#message{from = Peer, thread = <<"1">>,
body = [#text{data = <<"body">>}],
sub_els = [#chatstate{type = active}]}),
@@ -1617,14 +2197,14 @@ change_client_state(Config, NewState) ->
bookmark_conference() ->
#bookmark_conference{name = <<"Some name">>,
autojoin = true,
- jid = jlib:make_jid(
+ jid = jid:make(
<<"some">>,
<<"some.conference.org">>,
<<>>)}.
socks5_connect(#streamhost{host = Host, port = Port},
{SID, JID1, JID2}) ->
- Hash = p1_sha:sha([SID, jlib:jid_to_string(JID1), jlib:jid_to_string(JID2)]),
+ Hash = p1_sha:sha([SID, jid:to_string(JID1), jid:to_string(JID2)]),
{ok, Sock} = gen_tcp:connect(binary_to_list(Host), Port,
[binary, {active, false}]),
Init = <<?VERSION_5, 1, ?AUTH_ANONYMOUS>>,
@@ -1726,7 +2306,7 @@ clear_riak_tables(Config) ->
User = ?config(user, Config),
Server = ?config(server, Config),
Room = muc_room_jid(Config),
- {URoom, SRoom, _} = jlib:jid_tolower(Room),
+ {URoom, SRoom, _} = jid:tolower(Room),
ejabberd_auth:remove_user(User, Server),
ejabberd_auth:remove_user(<<"test_slave">>, Server),
ejabberd_auth:remove_user(<<"test_master">>, Server),
diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml
index 0a836b805..923d69024 100644
--- a/test/ejabberd_SUITE_data/ejabberd.yml
+++ b/test/ejabberd_SUITE_data/ejabberd.yml
@@ -8,12 +8,12 @@ host_config:
odbc_password: "@@pgsql_pass@@"
odbc_database: "@@pgsql_db@@"
auth_method: odbc
+ sm_db_type: odbc
modules:
mod_announce:
db_type: odbc
access: local
- mod_blocking:
- db_type: odbc
+ mod_blocking: []
mod_caps:
db_type: odbc
mod_last:
@@ -35,10 +35,13 @@ host_config:
- "flat"
- "hometree"
- "pep"
+ mod_mix: []
mod_roster:
versioning: true
store_current_id: true
db_type: odbc
+ mod_mam:
+ db_type: odbc
mod_vcard:
db_type: odbc
mod_vcard_xupdate:
@@ -59,12 +62,12 @@ Welcome to this XMPP server."
"sqlite.localhost":
odbc_type: sqlite
auth_method: odbc
+ sm_db_type: odbc
modules:
mod_announce:
db_type: odbc
access: local
- mod_blocking:
- db_type: odbc
+ mod_blocking: []
mod_caps:
db_type: odbc
mod_last:
@@ -86,10 +89,13 @@ Welcome to this XMPP server."
- "flat"
- "hometree"
- "pep"
+ mod_mix: []
mod_roster:
versioning: true
store_current_id: true
db_type: odbc
+ mod_mam:
+ db_type: odbc
mod_vcard:
db_type: odbc
mod_vcard_xupdate:
@@ -116,12 +122,12 @@ Welcome to this XMPP server."
odbc_password: "@@mysql_pass@@"
odbc_database: "@@mysql_db@@"
auth_method: odbc
+ sm_db_type: odbc
modules:
mod_announce:
db_type: odbc
access: local
- mod_blocking:
- db_type: odbc
+ mod_blocking: []
mod_caps:
db_type: odbc
mod_last:
@@ -143,10 +149,13 @@ Welcome to this XMPP server."
- "flat"
- "hometree"
- "pep"
+ mod_mix: []
mod_roster:
versioning: true
store_current_id: true
db_type: odbc
+ mod_mam:
+ db_type: odbc
mod_vcard:
db_type: odbc
mod_vcard_xupdate:
@@ -170,8 +179,7 @@ Welcome to this XMPP server."
mod_announce:
db_type: internal
access: local
- mod_blocking:
- db_type: internal
+ mod_blocking: []
mod_caps:
db_type: internal
mod_last:
@@ -192,16 +200,74 @@ Welcome to this XMPP server."
- "flat"
- "hometree"
- "pep"
+ mod_mix: []
mod_roster:
versioning: true
store_current_id: true
db_type: internal
+ mod_mam:
+ db_type: internal
mod_vcard:
db_type: internal
mod_vcard_xupdate:
db_type: internal
- mod_carboncopy:
+ mod_carboncopy: []
+ mod_client_state:
+ drop_chat_states: true
+ queue_presence: true
+ mod_adhoc: []
+ mod_configure: []
+ mod_disco: []
+ mod_ping: []
+ mod_proxy65: []
+ mod_register:
+ welcome_message:
+ subject: "Welcome!"
+ body: "Hi.
+Welcome to this XMPP server."
+ mod_stats: []
+ mod_time: []
+ mod_version: []
+ "redis.localhost":
+ auth_method: internal
+ sm_db_type: redis
+ modules:
+ mod_announce:
+ db_type: internal
+ access: local
+ mod_blocking: []
+ mod_caps:
+ db_type: internal
+ mod_last:
+ db_type: internal
+ mod_muc:
+ db_type: internal
+ mod_offline:
db_type: internal
+ mod_privacy:
+ db_type: internal
+ mod_private:
+ db_type: internal
+ mod_pubsub:
+ access_createnode: pubsub_createnode
+ ignore_pep_from_offline: true
+ last_item_cache: false
+ plugins:
+ - "flat"
+ - "hometree"
+ - "pep"
+ mod_mix: []
+ mod_roster:
+ versioning: true
+ store_current_id: true
+ db_type: internal
+ mod_mam:
+ db_type: internal
+ mod_vcard:
+ db_type: internal
+ mod_vcard_xupdate:
+ db_type: internal
+ mod_carboncopy: []
mod_client_state:
drop_chat_states: true
queue_presence: true
@@ -224,8 +290,7 @@ Welcome to this XMPP server."
mod_announce:
db_type: riak
access: local
- mod_blocking:
- db_type: riak
+ mod_blocking: []
mod_caps:
db_type: riak
mod_last:
@@ -290,6 +355,7 @@ Welcome to this XMPP server."
hosts:
- "localhost"
- "mnesia.localhost"
+ - "redis.localhost"
- "mysql.localhost"
- "pgsql.localhost"
- "extauth.localhost"
diff --git a/test/ejabberd_hooks_test.exs b/test/ejabberd_hooks_test.exs
index cbb126384..6493642de 100644
--- a/test/ejabberd_hooks_test.exs
+++ b/test/ejabberd_hooks_test.exs
@@ -1,6 +1,6 @@
# ----------------------------------------------------------------------
#
-# ejabberd, Copyright (C) 2002-2015 ProcessOne
+# ejabberd, Copyright (C) 2002-2016 ProcessOne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
@@ -18,30 +18,41 @@
#
# ----------------------------------------------------------------------
+# 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: true
-
+
@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
-
+ 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
@@ -51,7 +62,7 @@ defmodule EjabberdHooksTest do
test "An anonymous function can be removed from hook handlers" do
hookname = :test_fun_hook
- anon_fun = fn _ -> :ok end
+ 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)
@@ -77,20 +88,20 @@ defmodule EjabberdHooksTest do
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)
+ [{_pid, {^modulename, _callback, [:hook_params]}, :stop}] = :meck.history(modulename)
end
test "Run fold hooks accumulate state in correct order through handlers" do
@@ -99,10 +110,10 @@ defmodule EjabberdHooksTest do
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
@@ -115,12 +126,12 @@ defmodule EjabberdHooksTest do
: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
@@ -148,19 +159,19 @@ defmodule EjabberdHooksTest do
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
+ 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)
+ [{_pid, {^modulename, ^callback, ^params}, ^result}] = :meck.history(modulename)
end
def run_fold_crash(crash_fun) do
@@ -175,7 +186,7 @@ defmodule EjabberdHooksTest do
:final = :ejabberd_hooks.run_fold(hookname, @host, :started, [])
# Both handlers were called
- 2 = length(:meck.history(modulename))
+ 2 = length(:meck.history(modulename))
end
# TODO refactor: Move to ejabberd_test_mock
@@ -188,5 +199,5 @@ defmodule EjabberdHooksTest do
:meck.expect(module, function, fun)
end
-
+
end
diff --git a/test/elixir_SUITE.erl b/test/elixir_SUITE.erl
index b0a76bfa4..b9a0b1a23 100644
--- a/test/elixir_SUITE.erl
+++ b/test/elixir_SUITE.erl
@@ -1,6 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Mickael Remond <mremond@process-one.net>
-%%% @copyright (C) 2002-2015, ProcessOne
+%%% @copyright (C) 2002-2016, ProcessOne
%%% @doc
%%% This is a common test wrapper to run our ejabberd tests written in
%%% Elixir from standard common test code.
@@ -8,7 +8,7 @@
%%% Example: Is run with:
%%% ./rebar skip_deps=true ct suites=elixir
%%% or from ejabber overall test suite:
-%%% make test
+%%% make quicktest
%%% @end
%%% Created : 19 Feb 2015 by Mickael Remond <mremond@process-one.net>
%%%-------------------------------------------------------------------
@@ -17,6 +17,10 @@
-compile(export_all).
+init_per_suite(Config) ->
+ check_meck(),
+ Config.
+
init_per_testcase(_TestCase, Config) ->
process_flag(error_handler, ?MODULE),
Config.
@@ -32,9 +36,19 @@ all() ->
[]
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
@@ -55,7 +69,14 @@ run_elixir_test(Func) ->
'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
ResultMap = 'Elixir.ExUnit':run(),
- {ok, 0} = maps:find(failures, ResultMap).
+ case maps:find(failures, ResultMap) of
+ {ok, 0} ->
+ %% Zero failures
+ ok;
+ {ok, Failures} ->
+ ct:print("Elixir tests failed: ~.10B~nSee logs for details", [Failures]),
+ ct:fail(elixir_test_failure)
+ end.
test_dir() ->
{ok, CWD} = file:get_cwd(),
diff --git a/test/jid_test.exs b/test/jid_test.exs
new file mode 100644
index 000000000..b75a3603a
--- /dev/null
+++ b/test/jid_test.exs
@@ -0,0 +1,44 @@
+# ----------------------------------------------------------------------
+#
+# ejabberd, Copyright (C) 2002-2016 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: "ejabberd/include/jlib.hrl")
+
+ setup_all do
+ :stringprep.start
+ :jid.start
+ 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
diff --git a/test/ldap_srv.erl b/test/ldap_srv.erl
index 132f53e04..69b8a27e7 100644
--- a/test/ldap_srv.erl
+++ b/test/ldap_srv.erl
@@ -1,6 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2013, Evgeniy Khramtsov
+%%% @copyright (C) 2013-2016, Evgeniy Khramtsov
%%% @doc
%%% Simple LDAP server intended for LDAP modules testing
%%% @end
diff --git a/test/suite.erl b/test/suite.erl
index fd5e175ea..8000e1d29 100644
--- a/test/suite.erl
+++ b/test/suite.erl
@@ -1,6 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Evgeniy Khramtsov <>
-%%% @copyright (C) 2013, Evgeniy Khramtsov
+%%% @copyright (C) 2013-2016, Evgeniy Khramtsov
%%% @doc
%%%
%%% @end
@@ -65,9 +65,21 @@ init_config(Config) ->
{resource, <<"resource">>},
{master_resource, <<"master_resource">>},
{slave_resource, <<"slave_resource">>},
- {password, <<"password">>}
+ {password, <<"password">>},
+ {backends, get_config_backends()}
|Config].
+%% Read environment variable CT_DB=riak,mysql to limit the backends to test.
+%% You can thus limit the backend you want to test with:
+%% CT_BACKENDS=riak,mysql rebar ct suites=ejabberd
+get_config_backends() ->
+ case os:getenv("CT_BACKENDS") of
+ false -> all;
+ String ->
+ Backends0 = string:tokens(String, ","),
+ lists:map(fun(Backend) -> string:strip(Backend, both, $ ) end, Backends0)
+ end.
+
process_config_tpl(Content, []) ->
Content;
process_config_tpl(Content, [{Name, DefaultValue} | Rest]) ->
@@ -94,8 +106,8 @@ init_stream(Config) ->
ok = send_text(Config, io_lib:format(?STREAM_HEADER,
[?config(server, Config)])),
{xmlstreamstart, <<"stream:stream">>, Attrs} = recv(),
- <<"jabber:client">> = xml:get_attr_s(<<"xmlns">>, Attrs),
- <<"1.0">> = xml:get_attr_s(<<"version">>, Attrs),
+ <<"jabber:client">> = fxml:get_attr_s(<<"xmlns">>, Attrs),
+ <<"1.0">> = fxml:get_attr_s(<<"version">>, Attrs),
#stream_features{sub_els = Fs} = recv(),
Mechs = lists:flatmap(
fun(#sasl_mechanisms{list = Ms}) ->
@@ -182,8 +194,8 @@ wait_auth_SASL_result(Config) ->
io_lib:format(?STREAM_HEADER,
[?config(server, Config)])),
{xmlstreamstart, <<"stream:stream">>, Attrs} = recv(),
- <<"jabber:client">> = xml:get_attr_s(<<"xmlns">>, Attrs),
- <<"1.0">> = xml:get_attr_s(<<"version">>, Attrs),
+ <<"jabber:client">> = fxml:get_attr_s(<<"xmlns">>, Attrs),
+ <<"1.0">> = fxml:get_attr_s(<<"version">>, Attrs),
#stream_features{sub_els = Fs} = recv(),
lists:foldl(
fun(#feature_sm{}, ConfigAcc) ->
@@ -255,7 +267,7 @@ send(State, Pkt) ->
end,
El = xmpp_codec:encode(NewPkt),
ct:pal("sent: ~p <-~n~s", [El, xmpp_codec:pp(NewPkt)]),
- ok = send_text(State, xml:element_to_binary(El)),
+ ok = send_text(State, fxml:element_to_binary(El)),
NewID.
send_recv(State, IQ) ->
@@ -271,7 +283,7 @@ sasl_new(<<"DIGEST-MD5">>, User, Server, Password) ->
case cyrsasl_digest:parse(ServerIn) of
bad -> {error, <<"Invalid SASL challenge">>};
KeyVals ->
- Nonce = xml:get_attr_s(<<"nonce">>, KeyVals),
+ Nonce = fxml:get_attr_s(<<"nonce">>, KeyVals),
CNonce = id(),
Realm = proplists:get_value(<<"realm">>, KeyVals, Server),
DigestURI = <<"xmpp/", Realm/binary>>,
@@ -331,28 +343,36 @@ response(User, Passwd, Nonce, AuthzId, Realm, CNonce,
hex((erlang:md5(T))).
my_jid(Config) ->
- jlib:make_jid(?config(user, Config),
- ?config(server, Config),
- ?config(resource, Config)).
+ jid:make(?config(user, Config),
+ ?config(server, Config),
+ ?config(resource, Config)).
server_jid(Config) ->
- jlib:make_jid(<<>>, ?config(server, Config), <<>>).
+ jid:make(<<>>, ?config(server, Config), <<>>).
pubsub_jid(Config) ->
Server = ?config(server, Config),
- jlib:make_jid(<<>>, <<"pubsub.", Server/binary>>, <<>>).
+ jid:make(<<>>, <<"pubsub.", Server/binary>>, <<>>).
proxy_jid(Config) ->
Server = ?config(server, Config),
- jlib:make_jid(<<>>, <<"proxy.", Server/binary>>, <<>>).
+ jid:make(<<>>, <<"proxy.", Server/binary>>, <<>>).
muc_jid(Config) ->
Server = ?config(server, Config),
- jlib:make_jid(<<>>, <<"conference.", Server/binary>>, <<>>).
+ jid:make(<<>>, <<"conference.", Server/binary>>, <<>>).
muc_room_jid(Config) ->
Server = ?config(server, Config),
- jlib:make_jid(<<"test">>, <<"conference.", Server/binary>>, <<>>).
+ jid:make(<<"test">>, <<"conference.", Server/binary>>, <<>>).
+
+mix_jid(Config) ->
+ Server = ?config(server, Config),
+ jid:make(<<>>, <<"mix.", Server/binary>>, <<>>).
+
+mix_room_jid(Config) ->
+ Server = ?config(server, Config),
+ jid:make(<<"test">>, <<"mix.", Server/binary>>, <<>>).
id() ->
id(undefined).
diff --git a/test/suite.hrl b/test/suite.hrl
index 5463575a8..fb6b4f3ac 100644
--- a/test/suite.hrl
+++ b/test/suite.hrl
@@ -1,5 +1,5 @@
-include_lib("common_test/include/ct.hrl").
--include_lib("p1_xml/include/xml.hrl").
+-include_lib("fast_xml/include/fxml.hrl").
-include("ns.hrl").
-include("ejabberd.hrl").
-include("mod_proxy65.hrl").
@@ -74,6 +74,7 @@
-define(COMMON_VHOST, <<"localhost">>).
-define(MNESIA_VHOST, <<"mnesia.localhost">>).
+-define(REDIS_VHOST, <<"redis.localhost">>).
-define(MYSQL_VHOST, <<"mysql.localhost">>).
-define(PGSQL_VHOST, <<"pgsql.localhost">>).
-define(SQLITE_VHOST, <<"sqlite.localhost">>).