1: -module(mongoose_cleanup_SUITE). 2: 3: -include_lib("eunit/include/eunit.hrl"). 4: -include("mongoose.hrl"). 5: 6: -export([all/0, groups/0, 7: init_per_suite/1, end_per_suite/1, 8: init_per_group/2, end_per_group/2, 9: init_per_testcase/2, end_per_testcase/2]). 10: -export([cleaner_runs_hook_on_nodedown/1, 11: cleaner_runs_hook_on_nodedown_for_host_type/1, 12: notify_self_hook/3, 13: notify_self_hook_for_host_type/3]). 14: -export([auth_anonymous/1, 15: last/1, 16: stream_management/1, 17: s2s/1, 18: bosh/1, 19: component/1, 20: component_from_other_node_remains/1, 21: muc_node_cleanup_for_host_type/1, 22: muc_room/1, 23: muc_room_from_other_node_remains/1 24: ]). 25: 26: -define(HOST, <<"localhost">>). 27: -define(NS_CC_2, <<"urn:xmpp:carbons:2">>). 28: 29: %% ----------------------------------------------------- 30: %% CT callbacks 31: %% ----------------------------------------------------- 32: 33: all() -> 34: [ 35: cleaner_runs_hook_on_nodedown, 36: cleaner_runs_hook_on_nodedown_for_host_type, 37: auth_anonymous, 38: last, 39: stream_management, 40: s2s, 41: bosh, 42: [{group, Group} || {Group, _, _} <- groups()] 43: ]. 44: 45: groups() -> 46: [{component_cets, [], component_cases()}, 47: {component_mnesia, [], component_cases()}, 48: {muc_cets, [], muc_cases()}, 49: {muc_mnesia, [], muc_cases()}]. 50: 51: component_cases() -> 52: [component, component_from_other_node_remains]. 53: 54: muc_cases() -> 55: [muc_node_cleanup_for_host_type, muc_room, muc_room_from_other_node_remains]. 56: 57: init_per_suite(Config) -> 58: {ok, _} = application:ensure_all_started(jid), 59: ok = mnesia:create_schema([node()]), 60: ok = mnesia:start(), 61: mongoose_config:set_opts(opts()), 62: Config. 63: 64: end_per_suite(Config) -> 65: mongoose_config:erase_opts(), 66: mnesia:stop(), 67: mnesia:delete_schema([node()]), 68: Config. 69: 70: init_per_group(component_mnesia, Config) -> 71: mongoose_config:set_opt(component_backend, mnesia), 72: Config; 73: init_per_group(component_cets, Config) -> 74: mongoose_config:set_opt(component_backend, cets), 75: start_cets_disco(Config); 76: init_per_group(muc_mnesia, Config) -> 77: [{muc_backend, mnesia} | Config]; 78: init_per_group(muc_cets, Config) -> 79: [{muc_backend, cets} | start_cets_disco(Config)]. 80: 81: end_per_group(_Group, Config) -> 82: stop_cets_disco(Config). 83: 84: init_per_testcase(TestCase, Config) -> 85: mim_ct_sup:start_link(ejabberd_sup), 86: {ok, _HooksServer} = mongooseim_helper:start_link_loaded_hooks(), 87: {ok, _DomainSup} = mongoose_domain_sup:start_link(), 88: setup_meck(meck_mods(TestCase)), 89: start_component(TestCase), 90: start_muc(TestCase, Config), 91: Config. 92: 93: end_per_testcase(TestCase, _Config) -> 94: stop_component(TestCase), 95: mongoose_modules:stop(), 96: mongoose_config:set_opt({modules, ?HOST}, #{}), 97: unload_meck(meck_mods(TestCase)). 98: 99: start_component(TestCase) -> 100: case needs_component(TestCase) of 101: true -> 102: mongoose_router:start(), 103: mongoose_component:start(); 104: false -> 105: ok 106: end. 107: 108: stop_component(TestCase) -> 109: case needs_component(TestCase) of 110: true -> 111: mongoose_component:stop(); 112: false -> 113: ok 114: end. 115: 116: needs_component(TestCase) -> 117: lists:member(TestCase, component_cases()). 118: 119: opts() -> 120: #{hosts => [?HOST], 121: host_types => [], 122: all_metrics_are_global => false, 123: s2s_backend => mnesia, 124: {auth, ?HOST} => config_parser_helper:extra_auth(), 125: {modules, ?HOST} => #{}}. 126: 127: meck_mods(bosh) -> [exometer, mod_bosh_socket]; 128: meck_mods(s2s) -> [exometer, mongoose_bin]; 129: meck_mods(component) -> [exometer]; 130: meck_mods(_) -> [exometer, ejabberd_sm, ejabberd_local]. 131: 132: %% ----------------------------------------------------- 133: %% Tests 134: %% ----------------------------------------------------- 135: 136: cleaner_runs_hook_on_nodedown(_Config) -> 137: meck:expect(gen_hook, error_running_hook, fun(_, _, _, _, _) -> ok end), 138: {ok, Cleaner} = mongoose_cleaner:start_link(), 139: gen_hook:add_handler(node_cleanup, global, 140: fun ?MODULE:notify_self_hook/3, 141: #{self => self()}, 50), 142: FakeNode = fakename@fakehost, 143: Cleaner ! {nodedown, FakeNode}, 144: receive 145: {got_nodedown, FakeNode} -> ok 146: after timer:seconds(1) -> 147: ct:fail({timeout, got_nodedown}) 148: end, 149: ?assertEqual(false, meck:called(gen_hook, error_running_hook, 150: ['_', '_', '_', '_', '_'])). 151: 152: cleaner_runs_hook_on_nodedown_for_host_type(_Config) -> 153: HostType = ?HOST, 154: {ok, Cleaner} = mongoose_cleaner:start_link(), 155: gen_hook:add_handler(node_cleanup_for_host_type, HostType, 156: fun ?MODULE:notify_self_hook_for_host_type/3, 157: #{self => self()}, 50), 158: FakeNode = fakename@fakehost, 159: Cleaner ! {nodedown, FakeNode}, 160: receive 161: {got_nodedown_for_host_type, FakeNode, HostType} -> ok 162: after timer:seconds(1) -> 163: ct:fail({timeout, got_nodedown}) 164: end. 165: 166: notify_self_hook(Acc, #{node := Node}, #{self := Self}) -> 167: Self ! {got_nodedown, Node}, 168: {ok, Acc}. 169: 170: notify_self_hook_for_host_type(Acc, #{node := Node}, #{self := Self, host_type := HostType}) -> 171: Self ! {got_nodedown_for_host_type, Node, HostType}, 172: {ok, Acc}. 173: 174: auth_anonymous(_Config) -> 175: HostType = ?HOST, 176: {U, S, R, JID, SID} = get_fake_session(), 177: ejabberd_auth_anonymous:start(HostType), 178: Info = #{auth_module => cyrsasl_anonymous}, 179: ejabberd_auth_anonymous:register_connection(#{}, 180: #{sid => SID, jid => JID, info => Info}, 181: #{host_type => HostType}), 182: true = ejabberd_auth_anonymous:does_user_exist(HostType, U, S), 183: mongoose_hooks:session_cleanup(S, new_acc(S), U, R, SID), 184: false = ejabberd_auth_anonymous:does_user_exist(HostType, U, S). 185: 186: last(_Config) -> 187: HostType = ?HOST, 188: {U, S, R, JID, SID} = get_fake_session(), 189: {started, ok} = start(HostType, 190: mod_last, 191: config_parser_helper:mod_config(mod_last, #{iqdisc => no_queue})), 192: not_found = mod_last:get_last_info(HostType, U, S), 193: Status1 = <<"status1">>, 194: {ok, #{}} = mod_last:on_presence_update(new_acc(S), #{jid => JID, status => Status1}, #{}), 195: {ok, TS1, Status1} = mod_last:get_last_info(HostType, U, S), 196: async_helper:wait_until( 197: fun() -> 198: mongoose_hooks:session_cleanup(S, new_acc(S), U, R, SID), 199: {ok, TS2, <<>>} = mod_last:get_last_info(HostType, U, S), 200: TS2 - TS1 > 0 201: end, 202: true). 203: 204: stream_management(_Config) -> 205: HostType = ?HOST, 206: {U, S, R, _JID, SID} = get_fake_session(), 207: {started, ok} = start(HostType, mod_stream_management), 208: SMID = <<"123">>, 209: mod_stream_management:register_smid(HostType, SMID, SID), 210: {sid, SID} = mod_stream_management:get_sid(HostType, SMID), 211: mongoose_hooks:session_cleanup(S, new_acc(S), U, R, SID), 212: {error, smid_not_found} = mod_stream_management:get_sid(HostType, SMID). 213: 214: s2s(_Config) -> 215: ejabberd_s2s:start_link(), 216: FromTo = {?HOST, <<"foreign">>}, 217: ejabberd_s2s:try_register(FromTo), 218: Self = self(), 219: [Self] = ejabberd_s2s:get_s2s_out_pids(FromTo), 220: mongoose_hooks:node_cleanup(node()), 221: [] = ejabberd_s2s:get_s2s_out_pids(FromTo). 222: 223: bosh(_Config) -> 224: {started, ok} = start(?HOST, mod_bosh), 225: SID = <<"sid">>, 226: Self = self(), 227: {error, _} = mod_bosh:get_session_socket(SID), 228: mod_bosh:store_session(SID, Self), 229: {ok, Self} = mod_bosh:get_session_socket(SID), 230: mongoose_hooks:node_cleanup(node()), 231: {error, _} = mod_bosh:get_session_socket(SID), 232: ok. 233: 234: component(_Config) -> 235: Handler = fun() -> ok end, 236: Domain = <<"cool.localhost">>, 237: Node = some_node, 238: {ok, _} = mongoose_component:register_components([Domain], Node, Handler, false), 239: true = mongoose_component:has_component(Domain), 240: #{mongoose_component := ok} = mongoose_hooks:node_cleanup(Node), 241: [] = mongoose_component:dirty_get_all_components(all), 242: false = mongoose_component:has_component(Domain), 243: ok. 244: 245: component_from_other_node_remains(_Config) -> 246: Handler = fun() -> ok end, 247: Domain = <<"cool.localhost">>, 248: {ok, Comps} = mongoose_component:register_components([Domain], other_node, Handler, false), 249: true = mongoose_component:has_component(Domain), 250: #{mongoose_component := ok} = mongoose_hooks:node_cleanup(some_node), 251: true = mongoose_component:has_component(Domain), 252: mongoose_component:unregister_components(Comps), 253: ok. 254: 255: muc_node_cleanup_for_host_type(_Config) -> 256: {ok, Pid} = mongoose_cleaner:start_link(), 257: Pid ! {nodedown, 'badnode@localhost'}, 258: %% Check if the cleaner process is still running 259: ok = gen_server:call(Pid, ping). 260: 261: muc_room(_Config) -> 262: HostType = ?HOST, 263: MucHost = <<"muc.localhost">>, 264: Pid = remote_pid(), 265: Node = node(Pid), 266: Room = <<"remote_room">>, 267: ok = mod_muc_online_backend:register_room(HostType, MucHost, Room, Pid), 268: ok = mod_muc_online_backend:node_cleanup(HostType, Node), 269: {error, not_found} = mod_muc_online_backend:find_room_pid(HostType, MucHost, Room). 270: 271: muc_room_from_other_node_remains(_Config) -> 272: HostType = ?HOST, 273: MucHost = <<"muc.localhost">>, 274: Pid = self(), 275: RemoteNode = node(remote_pid()), 276: Room = <<"room_on_other_node">>, 277: ok = mod_muc_online_backend:register_room(HostType, MucHost, Room, Pid), 278: ok = mod_muc_online_backend:node_cleanup(HostType, RemoteNode), 279: {ok, Pid} = mod_muc_online_backend:find_room_pid(HostType, MucHost, Room). 280: 281: %% ----------------------------------------------------- 282: %% Internal 283: %% ----------------------------------------------------- 284: 285: setup_meck([exometer | R]) -> 286: meck:new(exometer), 287: meck:expect(exometer, info, fun(_, _) -> undefined end), 288: meck:expect(exometer, new, fun(_, _) -> ok end), 289: meck:expect(exometer, update, fun(_, _) -> ok end), 290: setup_meck(R); 291: setup_meck([ejabberd_sm | R]) -> 292: meck:new(ejabberd_sm), 293: meck:expect(ejabberd_sm, register_iq_handler, 294: fun(_A1, _A2, _A3) -> ok end), 295: setup_meck(R); 296: setup_meck([ejabberd_local | R]) -> 297: meck:new(ejabberd_local), 298: meck:expect(ejabberd_local, register_iq_handler, 299: fun(_A1, _A2, _A3) -> ok end), 300: setup_meck(R); 301: setup_meck([mongoose_bin | R]) -> 302: meck:new(mongoose_bin, [passthrough]), 303: meck:expect(mongoose_bin, gen_from_crypto, fun() -> <<"123456">> end), 304: setup_meck(R); 305: setup_meck([mod_bosh_socket | R]) -> 306: meck:new(mod_bosh_socket, [passthrough]), 307: meck:expect(mod_bosh_socket, start_supervisor, fun() -> {ok, self()} end), 308: setup_meck(R); 309: setup_meck([]) -> 310: ok. 311: 312: unload_meck(Mods) -> 313: [ {meck:validate(Mod), meck:unload(Mod)} || 314: Mod <- Mods ]. 315: 316: -spec get_fake_session() -> 317: {U :: binary(), S :: binary(), R :: binary(), 318: JID :: jid:jid(), SID :: ejabberd_sm:sid()}. 319: get_fake_session() -> 320: U = <<"someuser">>, 321: S = ?HOST, 322: R = <<"someresource">>, 323: JID = jid:make(U, S, R), 324: SID = {os:timestamp(), self()}, 325: {U, S, R, JID, SID}. 326: 327: new_acc(Server) -> 328: mongoose_acc:new(#{location => ?LOCATION, 329: lserver => Server, 330: host_type => ?HOST, 331: element => undefined}). 332: 333: start(HostType, Module) -> 334: start(HostType, Module, config_parser_helper:default_mod_config(Module)). 335: 336: start(HostType, Module, Opts) -> 337: mongoose_modules:ensure_started(HostType, Module, Opts). 338: 339: disco_opts() -> 340: #{name => mongoose_cets_discovery, disco_file => "does_not_exist.txt"}. 341: 342: start_cets_disco(Config) -> 343: {ok, Pid} = cets_discovery:start(disco_opts()), 344: [{cets_disco, Pid} | Config]. 345: 346: stop_cets_disco(Config) -> 347: case proplists:get_value(cets_disco, Config) of 348: Pid when is_pid(Pid) -> 349: exit(Pid, kill); 350: _ -> 351: ok 352: end. 353: 354: %% Pid 90 on cool_node@localhost 355: %% Made using: 356: %% erl -name cool_node@localhost 357: %% rp(term_to_binary(list_to_pid("<0.90.0>"))). 358: remote_pid_binary() -> 359: <<131, 88, 100, 0, 19, 99, 111, 111, 108, 95, 110, 111, 100, 101, 64, 360: 108, 111, 99, 97, 108, 104, 111, 115, 116, 0, 0, 0, 90, 0, 0, 0, 0, 100, 361: 200, 255, 233>>. 362: 363: remote_pid() -> 364: binary_to_term(remote_pid_binary()). 365: 366: start_muc(TestCase, Config) -> 367: case proplists:get_value(muc_backend, Config) of 368: undefined -> ok; 369: Backend -> 370: case should_start_full_muc_module(TestCase) of 371: true -> 372: start_muc_module(Backend); 373: false -> 374: start_muc_backend(Backend) 375: end 376: end. 377: 378: start_muc_backend(Backend) -> 379: mod_muc_online_backend:start(?HOST, #{online_backend => Backend}). 380: 381: start_muc_module(Backend) -> 382: ExtraOpts = #{online_backend => Backend, backend => mnesia}, 383: Opts = config_parser_helper:mod_config(mod_muc, ExtraOpts), 384: {started, ok} = start(?HOST, mod_muc, Opts). 385: 386: should_start_full_muc_module(TestCase) -> 387: lists:member(TestCase, [muc_node_cleanup_for_host_type]).