diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/ejabberd_SUITE.erl | 730 | ||||
-rw-r--r-- | test/ejabberd_SUITE_data/ejabberd.yml | 88 | ||||
-rw-r--r-- | test/ejabberd_hooks_test.exs | 53 | ||||
-rw-r--r-- | test/elixir_SUITE.erl | 27 | ||||
-rw-r--r-- | test/jid_test.exs | 44 | ||||
-rw-r--r-- | test/ldap_srv.erl | 2 | ||||
-rw-r--r-- | test/suite.erl | 52 | ||||
-rw-r--r-- | test/suite.hrl | 3 |
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">>). |