summaryrefslogtreecommitdiff
path: root/test/push_tests.erl
diff options
context:
space:
mode:
authorHolger Weiss <holger@zedat.fu-berlin.de>2017-07-20 20:22:50 +0200
committerHolger Weiss <holger@zedat.fu-berlin.de>2017-07-20 20:22:50 +0200
commitd6f1d3df5b5a75f618bcc6eeb6425bc47dfd84d2 (patch)
tree561889efbb51eee8f164177369c92a174c5d7084 /test/push_tests.erl
parentFix errors when running ejabberdctl as root (diff)
Support XEP-0357: Push Notifications
Closes #1379.
Diffstat (limited to 'test/push_tests.erl')
-rw-r--r--test/push_tests.erl232
1 files changed, 232 insertions, 0 deletions
diff --git a/test/push_tests.erl b/test/push_tests.erl
new file mode 100644
index 00000000..535671ee
--- /dev/null
+++ b/test/push_tests.erl
@@ -0,0 +1,232 @@
+%%%-------------------------------------------------------------------
+%%% Author : Holger Weiss <holger@zedat.fu-berlin.de>
+%%% Created : 15 Jul 2017 by Holger Weiss <holger@zedat.fu-berlin.de>
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2017 ProcessOne
+%%%
+%%% This program is free software; you can redistribute it and/or
+%%% modify it under the terms of the GNU General Public License as
+%%% published by the Free Software Foundation; either version 2 of the
+%%% License, or (at your option) any later version.
+%%%
+%%% This program is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%%% General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License along
+%%% with this program; if not, write to the Free Software Foundation, Inc.,
+%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+%%%
+%%%----------------------------------------------------------------------
+
+-module(push_tests).
+
+%% API
+-compile(export_all).
+-import(suite, [close_socket/1, connect/1, disconnect/1, get_event/1,
+ get_features/2, make_iq_result/1, my_jid/1, put_event/2, recv/1,
+ recv_iq/1, recv_message/1, self_presence/2, send/2, send_recv/2,
+ server_jid/1]).
+
+-include("suite.hrl").
+
+-define(PUSH_NODE, <<"d3v1c3">>).
+-define(PUSH_XDATA_FIELDS,
+ [#xdata_field{var = <<"FORM_TYPE">>,
+ values = [?NS_PUBSUB_PUBLISH_OPTIONS]},
+ #xdata_field{var = <<"secret">>,
+ values = [<<"c0nf1d3nt14l">>]}]).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+%%%===================================================================
+%%% Single user tests
+%%%===================================================================
+single_cases() ->
+ {push_single, [sequence],
+ [single_test(feature_enabled),
+ single_test(unsupported_iq)]}.
+
+feature_enabled(Config) ->
+ BareMyJID = jid:remove_resource(my_jid(Config)),
+ Features = get_features(Config, BareMyJID),
+ true = lists:member(?NS_PUSH_0, Features),
+ disconnect(Config).
+
+unsupported_iq(Config) ->
+ PushJID = my_jid(Config),
+ lists:foreach(
+ fun(SubEl) ->
+ #iq{type = error} =
+ send_recv(Config, #iq{type = get, sub_els = [SubEl]})
+ end, [#push_enable{jid = PushJID}, #push_disable{jid = PushJID}]),
+ disconnect(Config).
+
+%%%===================================================================
+%%% Master-slave tests
+%%%===================================================================
+master_slave_cases() ->
+ {push_master_slave, [sequence],
+ [master_slave_test(sm),
+ master_slave_test(offline),
+ master_slave_test(mam)]}.
+
+sm_master(Config) ->
+ ct:comment("Waiting for the slave to close the socket"),
+ peer_down = get_event(Config),
+ ct:comment("Sending message to the slave"),
+ send_test_message(Config),
+ ct:comment("Handling push notification"),
+ handle_notification(Config),
+ ct:comment("Receiving bounced message from the slave"),
+ #message{type = error} = recv_message(Config),
+ ct:comment("Closing the connection"),
+ disconnect(Config).
+
+sm_slave(Config) ->
+ ct:comment("Enabling push notifications"),
+ ok = enable_push(Config),
+ ct:comment("Enabling stream management"),
+ ok = enable_sm(Config),
+ ct:comment("Closing the socket"),
+ close_socket(Config).
+
+offline_master(Config) ->
+ ct:comment("Waiting for the slave to be ready"),
+ ready = get_event(Config),
+ ct:comment("Sending message to the slave"),
+ send_test_message(Config), % No push notification, slave is online.
+ ct:comment("Waiting for the slave to disconnect"),
+ peer_down = get_event(Config),
+ ct:comment("Sending message to offline storage"),
+ send_test_message(Config),
+ ct:comment("Handling push notification for offline message"),
+ handle_notification(Config),
+ ct:comment("Closing the connection"),
+ disconnect(Config).
+
+offline_slave(Config) ->
+ ct:comment("Re-enabling push notifications"),
+ ok = enable_push(Config),
+ ct:comment("Letting the master know that we're ready"),
+ put_event(Config, ready),
+ ct:comment("Receiving message from the master"),
+ recv_test_message(Config),
+ ct:comment("Closing the connection"),
+ disconnect(Config).
+
+mam_master(Config) ->
+ ct:comment("Waiting for the slave to be ready"),
+ ready = get_event(Config),
+ ct:comment("Sending message to the slave"),
+ send_test_message(Config),
+ ct:comment("Handling push notification for MAM message"),
+ handle_notification(Config),
+ ct:comment("Closing the connection"),
+ disconnect(Config).
+
+mam_slave(Config) ->
+ self_presence(Config, available),
+ ct:comment("Receiving message from offline storage"),
+ recv_test_message(Config),
+ ct:comment("Re-enabling push notifications"),
+ ok = enable_push(Config),
+ ct:comment("Enabling MAM"),
+ ok = enable_mam(Config),
+ ct:comment("Letting the master know that we're ready"),
+ put_event(Config, ready),
+ ct:comment("Receiving message from the master"),
+ recv_test_message(Config),
+ ct:comment("Waiting for the master to disconnect"),
+ peer_down = get_event(Config),
+ ct:comment("Disabling push notifications"),
+ ok = disable_push(Config),
+ ct:comment("Closing the connection and cleaning up"),
+ clean(disconnect(Config)).
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+single_test(T) ->
+ list_to_atom("push_" ++ atom_to_list(T)).
+
+master_slave_test(T) ->
+ {list_to_atom("push_" ++ atom_to_list(T)), [parallel],
+ [list_to_atom("push_" ++ atom_to_list(T) ++ "_master"),
+ list_to_atom("push_" ++ atom_to_list(T) ++ "_slave")]}.
+
+enable_sm(Config) ->
+ send(Config, #sm_enable{xmlns = ?NS_STREAM_MGMT_3, resume = true}),
+ case recv(Config) of
+ #sm_enabled{resume = true} ->
+ ok;
+ #sm_failed{reason = Reason} ->
+ Reason
+ end.
+
+enable_mam(Config) ->
+ case send_recv(
+ Config, #iq{type = set, sub_els = [#mam_prefs{xmlns = ?NS_MAM_1,
+ default = always}]}) of
+ #iq{type = result} ->
+ ok;
+ #iq{type = error} = Err ->
+ xmpp:get_error(Err)
+ end.
+
+enable_push(Config) ->
+ %% Usually, the push JID would be a server JID (such as push.example.com).
+ %% We specify the peer's full user JID instead, so the push notifications
+ %% will be sent to the peer.
+ PushJID = ?config(peer, Config),
+ XData = #xdata{type = submit, fields = ?PUSH_XDATA_FIELDS},
+ case send_recv(
+ Config, #iq{type = set,
+ sub_els = [#push_enable{jid = PushJID,
+ node = ?PUSH_NODE,
+ xdata = XData}]}) of
+ #iq{type = result, sub_els = []} ->
+ ok;
+ #iq{type = error} = Err ->
+ xmpp:get_error(Err)
+ end.
+
+disable_push(Config) ->
+ PushJID = ?config(peer, Config),
+ case send_recv(
+ Config, #iq{type = set,
+ sub_els = [#push_disable{jid = PushJID,
+ node = ?PUSH_NODE}]}) of
+ #iq{type = result, sub_els = []} ->
+ ok;
+ #iq{type = error} = Err ->
+ xmpp:get_error(Err)
+ end.
+
+send_test_message(Config) ->
+ Peer = ?config(peer, Config),
+ Msg = #message{to = Peer, body = [#text{data = <<"test">>}]},
+ send(Config, Msg).
+
+recv_test_message(Config) ->
+ Peer = ?config(peer, Config),
+ #message{from = Peer,
+ body = [#text{data = <<"test">>}]} = recv_message(Config).
+
+handle_notification(Config) ->
+ From = server_jid(Config),
+ Item = #ps_item{xml_els = [xmpp:encode(#push_notification{})]},
+ Publish = #ps_publish{node = ?PUSH_NODE, items = [Item]},
+ XData = #xdata{type = submit, fields = ?PUSH_XDATA_FIELDS},
+ PubSub = #pubsub{publish = Publish, publish_options = XData},
+ IQ = #iq{type = set, from = From, sub_els = [PubSub]} = recv_iq(Config),
+ send(Config, make_iq_result(IQ)).
+
+clean(Config) ->
+ {U, S, _} = jid:tolower(my_jid(Config)),
+ mod_push:remove_user(U, S),
+ mod_mam:remove_user(U, S),
+ Config.