1: -module(mongoose_config_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("eunit/include/eunit.hrl"). 5: 6: -import(ejabberd_helper, [start_ejabberd_with_config/2, 7: use_config_file/2, 8: copy/2, 9: data/2]). 10: 11: all() -> 12: [{group, opts}, 13: {group, cluster}]. 14: 15: groups() -> 16: [ 17: {opts, [get_opt, 18: lookup_opt, 19: get_path, 20: lookup_path, 21: set_short_path, 22: set_long_path, 23: unset_path, 24: load_from_file]}, 25: {cluster, [], [cluster_load_from_file]} 26: ]. 27: 28: init_per_suite(Config) -> 29: mnesia:start(), %% TODO Remove this call when possible (We still need it for s2s) 30: {ok, _} = application:ensure_all_started(jid), 31: mongoose_config:set_opts(#{}), 32: Config. 33: 34: end_per_suite(_Config) -> 35: mongoose_config:erase_opts(), 36: mnesia:stop(), 37: mnesia:delete_schema([node()]), 38: ok. 39: 40: init_per_testcase(_TestCase, Config) -> 41: Config. 42: 43: end_per_testcase(_TestCase, _Config) -> 44: ok. 45: 46: init_per_group(cluster, Config) -> 47: start_slave_node(Config); 48: init_per_group(_GroupName, Config) -> 49: Config. 50: 51: end_per_group(cluster, Config) -> 52: stop_slave_node(Config), 53: ok; 54: end_per_group(_GroupName, _Config) -> 55: ok. 56: 57: %% 58: %% Tests 59: %% 60: 61: get_opt(_Config) -> 62: ?assertError({badkey, get_me}, mongoose_config:get_opt(get_me)), 63: ?assertEqual(default_value, mongoose_config:get_opt(get_me, default_value)), 64: mongoose_config:set_opt(get_me, you_got_me), 65: ?assertEqual(you_got_me, mongoose_config:get_opt(get_me)), 66: mongoose_config:set_opt(get_me, you_got_me_again), 67: ?assertEqual(you_got_me_again, mongoose_config:get_opt(get_me)), 68: ?assertEqual(you_got_me_again, mongoose_config:get_opt(get_me, default_value)), 69: mongoose_config:unset_opt(get_me), 70: ?assertError({badkey, get_me}, mongoose_config:get_opt(get_me)), 71: ?assertEqual(default_value, mongoose_config:get_opt(get_me, default_value)). 72: 73: lookup_opt(_Config) -> 74: ?assertEqual({error, not_found}, mongoose_config:lookup_opt(look_me_up)), 75: mongoose_config:set_opt(look_me_up, here_i_am), 76: ?assertEqual({ok, here_i_am}, mongoose_config:lookup_opt(look_me_up)), 77: mongoose_config:unset_opt(look_me_up), 78: ?assertEqual({error, not_found}, mongoose_config:lookup_opt(look_me_up)). 79: 80: get_path(_Config) -> 81: ?assertError({badkey, root}, mongoose_config:get_opt([root])), 82: ?assertError({badkey, root}, mongoose_config:get_opt([root, branch])), 83: mongoose_config:set_opt(root, #{branch => leaf}), 84: ?assertEqual(#{branch => leaf}, mongoose_config:get_opt([root])), 85: ?assertEqual(leaf, mongoose_config:get_opt([root, branch])), 86: ?assertError({badmap, leaf}, mongoose_config:get_opt([root, branch, leaf])), 87: mongoose_config:unset_opt(root), 88: ?assertError({badkey, root}, mongoose_config:get_opt([root])). 89: 90: lookup_path(_Config) -> 91: ?assertEqual({error, not_found}, mongoose_config:lookup_opt([basement])), 92: ?assertEqual({error, not_found}, mongoose_config:lookup_opt([basement, floor])), 93: mongoose_config:set_opt(basement, #{floor => roof}), 94: ?assertEqual({ok, #{floor => roof}}, mongoose_config:lookup_opt([basement])), 95: ?assertEqual({ok, roof}, mongoose_config:lookup_opt([basement, floor])), 96: ?assertError({badmap, roof}, mongoose_config:lookup_opt([basement, floor, roof])), 97: mongoose_config:unset_opt(basement), 98: ?assertEqual({error, not_found}, mongoose_config:lookup_opt([basement])). 99: 100: set_short_path(_Config) -> 101: mongoose_config:set_opt([a], 1), 102: ?assertEqual(1, mongoose_config:get_opt(a)), 103: mongoose_config:set_opt([a], 2), 104: ?assertEqual(2, mongoose_config:get_opt(a)), 105: ?assertError({badmap, 2}, mongoose_config:set_opt([a, b], c)), 106: ?assertEqual(2, mongoose_config:get_opt(a)). 107: 108: set_long_path(_Config) -> 109: ?assertError({badkey, one}, mongoose_config:set_opt([one, two, three], 4)), 110: mongoose_config:set_opt([one], #{}), 111: ?assertError({badkey, two}, mongoose_config:set_opt([one, two, three], 4)), 112: mongoose_config:set_opt([one, two], #{}), 113: mongoose_config:set_opt([one, two, three], 4), 114: ?assertEqual(#{two => #{three => 4}}, mongoose_config:get_opt(one)), 115: mongoose_config:set_opt([one, two], 3), 116: ?assertEqual(#{two => 3}, mongoose_config:get_opt(one)). 117: 118: unset_path(_Config) -> 119: mongoose_config:set_opt(foo, #{bar => #{baz => boom}}), 120: ?assertEqual(#{bar => #{baz => boom}}, mongoose_config:get_opt(foo)), 121: ?assertError({badmap, boom}, mongoose_config:unset_opt([foo, bar, baz, boom])), 122: mongoose_config:unset_opt([foo, bar, baz]), 123: ?assertEqual(#{bar => #{}}, mongoose_config:get_opt(foo)), % empty map is not removed 124: mongoose_config:unset_opt([foo, bar, baz]), % no error for a non-existing key 125: ?assertEqual(#{bar => #{}}, mongoose_config:get_opt(foo)), 126: mongoose_config:unset_opt([foo]), 127: ?assertError({badkey, foo}, mongoose_config:get_opt(foo)), 128: ?assertError({badkey, foo}, mongoose_config:unset_opt([foo, bar])), 129: mongoose_config:unset_opt([foo]). % no error for a non-existing key 130: 131: load_from_file(Config) -> 132: use_config_file(Config, "mongooseim.minimal.toml"), 133: ok = mongoose_config:start(), 134: Opts = mongoose_config:get_opts(), 135: check_loaded_config(Opts), 136: 137: ok = mongoose_config:stop(), 138: check_removed_config(), 139: 140: %% Try to stop it again 141: {error, not_started} = mongoose_config:stop(). 142: 143: cluster_load_from_file(Config) -> 144: SlaveNode = slave_node(Config), 145: copy(data(Config, "mongooseim.minimal.toml"), data(Config, "mongooseim.toml")), 146: 147: %% Start clustered MongooseIM and check the loaded config 148: {ok, _} = start_ejabberd_with_config(Config, "mongooseim.toml"), 149: {ok, _} = start_remote_ejabberd_with_config(SlaveNode, Config, "mongooseim.toml"), 150: maybe_join_cluster(SlaveNode), 151: check_loaded_config(mongoose_config:get_opts()), 152: check_loaded_config(rpc:call(SlaveNode, mongoose_config, get_opts, [])), 153: 154: ok = mongooseim:stop(), 155: stop_remote_ejabberd(SlaveNode), 156: check_removed_config(). 157: 158: %% 159: %% Helpers 160: %% 161: 162: check_loaded_config(Opts) -> 163: ?assertEqual(minimal_config_opts(), Opts). 164: 165: check_removed_config() -> 166: ?assertError(badarg, mongoose_config:get_opts()). 167: 168: minimal_config_opts() -> 169: #{all_metrics_are_global => false, 170: default_server_domain => <<"localhost">>, 171: hide_service_name => false, 172: host_types => [], 173: hosts => [<<"localhost">>], 174: internal_databases => #{mnesia => #{}}, 175: language => <<"en">>, 176: listen => [], 177: loglevel => warning, 178: outgoing_pools => [], 179: rdbms_server_type => generic, 180: registration_timeout => 600, 181: routing_modules => mongoose_router:default_routing_modules(), 182: services => #{}, 183: sm_backend => mnesia, 184: component_backend => mnesia, 185: s2s_backend => mnesia, 186: {auth, <<"localhost">>} => config_parser_helper:default_auth(), 187: {modules, <<"localhost">>} => #{}, 188: {replaced_wait_timeout, <<"localhost">>} => 2000, 189: {s2s, <<"localhost">>} => config_parser_helper:default_s2s()}. 190: 191: start_slave_node(Config) -> 192: SlaveNode = do_start_slave_node(), 193: [{slave_node, SlaveNode}|Config]. 194: 195: do_start_slave_node() -> 196: Opts = [{monitor_master, true}, 197: {boot_timeout, 15}, %% in seconds 198: {init_timeout, 10}, %% in seconds 199: {startup_timeout, 10}], %% in seconds 200: {ok, SlaveNode} = ct_slave:start(slave_name(), Opts), 201: rpc:call(SlaveNode, mnesia, start, []), %% TODO remove this call when possible 202: {ok, CWD} = file:get_cwd(), 203: ok = rpc:call(SlaveNode, file, set_cwd, [CWD]), 204: %% Tell the remote node where to find the SUITE code 205: %% Be aware, that P1 likes to put there stuff into 206: %% /usr/lib/erlang/lib/ 207: %% So add_paths is NOT enough here 208: ok = rpc:call(SlaveNode, code, add_pathsa, [lists:reverse(code_paths())]), 209: check_that_p1_tls_is_correct(SlaveNode), 210: SlaveNode. 211: 212: check_that_p1_tls_is_correct(SlaveNode) -> 213: ?assertEqual(fast_tls:module_info(md5), 214: rpc:call(SlaveNode, fast_tls, module_info, [md5])). 215: 216: stop_slave_node(Config) -> 217: ct_slave:stop(slave_node(Config)), 218: ok. 219: 220: slave_node(Config) -> 221: get_required_config(slave_node, Config). 222: 223: get_required_config(Key, Config) -> 224: case proplists:get_value(Key, Config) of 225: undefined -> 226: ct:fail({get_required_config_failed, Key}); 227: Value -> 228: Value 229: end. 230: 231: slave_name() -> 232: 'mim_slave'. 233: 234: start_remote_ejabberd_with_config(RemoteNode, C, ConfigFile) -> 235: rpc:call(RemoteNode, ejabberd_helper, start_ejabberd_with_config, [C, ConfigFile]). 236: 237: stop_remote_ejabberd(SlaveNode) -> 238: rpc:call(SlaveNode, mongooseim, stop, []). 239: 240: code_paths() -> 241: [filename:absname(Path) || Path <- code:get_path()]. 242: 243: maybe_join_cluster(SlaveNode) -> 244: Result = rpc:call(SlaveNode, mongoose_server_api, join_cluster, 245: [atom_to_list(node())]), 246: case Result of 247: {ok, _} -> 248: ok; 249: {already_joined, _} -> 250: ok 251: end. 252: