1: -module(router_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("exml/include/exml.hrl"). 5: -include_lib("eunit/include/eunit.hrl"). 6: -include("mongoose.hrl"). 7: 8: %% --------------------------------------------------------------- 9: %% Common Test callbacks 10: %% --------------------------------------------------------------- 11: 12: all() -> 13: [ 14: {group, routing} 15: ]. 16: 17: groups() -> 18: [ 19: {routing, [], [ 20: basic_routing, 21: do_not_reroute_errors 22: ]} 23: ]. 24: 25: init_per_suite(C) -> 26: {ok, _} = application:ensure_all_started(jid), 27: ok = mnesia:create_schema([node()]), 28: ok = mnesia:start(), 29: {ok, _} = application:ensure_all_started(exometer_core), 30: C. 31: 32: end_per_suite(_C) -> 33: meck:unload(), 34: mnesia:stop(), 35: mnesia:delete_schema([node()]), 36: application:stop(exometer_core), 37: ok. 38: 39: init_per_group(routing, Config) -> 40: mongoose_config:set_opts(opts()), 41: mongooseim_helper:start_link_loaded_hooks(), 42: ejabberd_router:start_link(), 43: Config. 44: 45: end_per_group(routing, _Config) -> 46: mongoose_config:erase_opts(). 47: 48: init_per_testcase(_CaseName, Config) -> 49: Config. 50: 51: end_per_testcase(_CaseName, _Config) -> 52: ok. 53: 54: %% --------------------------------------------------------------- 55: %% Test cases 56: %% --------------------------------------------------------------- 57: 58: basic_routing(_C) -> 59: %% module 'a' drops message 1, routes message 2, passes on everything else 60: setup_routing_module(xmpp_router_a, 1, 2), 61: %% module 'b' drops message 3, routes message 4, passes on everything else 62: setup_routing_module(xmpp_router_b, 3, 4), 63: %% module 'c' routes everything 64: setup_routing_module(xmpp_router_c, none, all), 65: %% send messages from 1 to 5 66: lists:map(fun(I) -> route(msg(I)) end, [1,2,3,4,5]), 67: meck:validate(xmpp_router_a), 68: meck:unload(xmpp_router_a), 69: meck:validate(xmpp_router_b), 70: meck:unload(xmpp_router_b), 71: meck:validate(xmpp_router_c), 72: meck:unload(xmpp_router_c), 73: %% we know that 1 and 3 should be dropped, and 2, 4 and 5 handled by a, b and c respectively 74: verify([{a, 2}, {b, 4}, {c, 5}]), 75: ok. 76: 77: %% This test makes sure that if we try to respond to an error message by routing error message 78: %% we do not enter an infinite loop; it has been fixed in d3941e33453c95ca78561144182712cc4f1d6c72 79: %% without the fix this tests gets stuck in a loop. 80: do_not_reroute_errors(_) -> 81: From = <<"ja@localhost">>, 82: To = <<"ty@localhost">>, 83: Stanza = #xmlel{name = <<"iq">>, 84: attrs = [{<<"from">>, From}, {<<"to">>, To}, {<<"type">>, <<"get">>} ] 85: }, 86: Acc = mongoose_acc:new(#{ location => ?LOCATION, 87: lserver => <<"localhost">>, 88: host_type => <<"localhost">>, 89: element => Stanza }), 90: meck:new(xmpp_router_a, [non_strict]), 91: meck:expect(xmpp_router_a, filter, 92: fun(From0, To0, Acc0, Packet0) -> {From0, To0, Acc0, Packet0} end), 93: meck:expect(xmpp_router_a, route, fun resend_as_error/4), 94: ejabberd_router:route(From, To, Acc, Stanza), 95: ok. 96: 97: %% --------------------------------------------------------------- 98: %% Helpers 99: %% --------------------------------------------------------------- 100: 101: setup_routing_module(Name, PacketToDrop, PacketToRoute) -> 102: meck:new(Name, [non_strict]), 103: meck:expect(Name, filter, 104: fun(From, To, Acc, Packet) -> 105: case msg_to_id(Packet) of 106: PacketToDrop -> drop; 107: _ -> {From, To, Acc, Packet} 108: end 109: end), 110: meck:expect(Name, route, 111: make_routing_fun(Name, PacketToRoute)), 112: ok. 113: 114: make_routing_fun(Name, all) -> 115: Self = self(), 116: Marker = list_to_atom([lists:last(atom_to_list(Name))]), 117: fun(_From, _To, Acc, Packet) -> 118: Self ! {Marker, Packet}, 119: {done, Acc} 120: end; 121: make_routing_fun(Name, PacketToRoute) -> 122: Self = self(), 123: Marker = list_to_atom([lists:last(atom_to_list(Name))]), 124: fun(From, To, Acc, Packet) -> 125: case msg_to_id(Packet) of 126: PacketToRoute -> 127: Self ! {Marker, Packet}, 128: {done, Acc}; 129: _ -> {From, To, Acc, Packet} 130: end 131: end. 132: 133: msg(I) -> 134: IBin = integer_to_binary(I), 135: #xmlel{ name = <<"message">>, 136: children = [ 137: #xmlel{ name = <<"body">>, 138: children = [#xmlcdata{ content = IBin }] } 139: ] }. 140: 141: msg_to_id(Msg) -> 142: binary_to_integer(exml_query:path(Msg, [{element, <<"body">>}, cdata])). 143: 144: route(I) -> 145: FromJID = jid:from_binary(<<"ala@localhost">>), 146: ToJID = jid:from_binary(<<"bob@localhost">>), 147: Acc = mongoose_acc:new(#{ location => ?LOCATION, 148: lserver => <<"localhost">>, 149: host_type => <<"localhost">>, 150: element => I, 151: from_jid => FromJID, 152: to_jid => ToJID }), 153: #{} = ejabberd_router:route(FromJID, ToJID, Acc, I). 154: 155: verify(L) -> 156: receive 157: {RouterID, XML} -> 158: X = msg_to_id(XML), 159: ct:pal("{RouterID, X, L}: ~p", [{RouterID, X, L}]), 160: Item = {RouterID, X}, 161: ?assert(lists:member(Item, L)), 162: verify(lists:delete(Item, L)) 163: after 1000 -> 164: ?assertEqual(L, []), 165: ct:pal("all messages routed correctly") 166: end. 167: 168: resend_as_error(From0, To0, Acc0, Packet0) -> 169: {Acc1, Packet1} = jlib:make_error_reply(Acc0, Packet0, #xmlel{}), 170: Acc2 = ejabberd_router:route(To0, From0, Acc1, Packet1), 171: {done, Acc2}. 172: 173: opts() -> 174: RoutingModules = [xmpp_router_a, xmpp_router_b, xmpp_router_c], 175: #{all_metrics_are_global => false, 176: component_backend => mnesia, 177: routing_modules => xmpp_router:expand_routing_modules(RoutingModules)}.