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