1: -module(config_parser_SUITE).
    2: -compile([export_all, nowarn_export_all]).
    3: 
    4: -include_lib("eunit/include/eunit.hrl").
    5: 
    6: -define(HOST_TYPE, <<"my host type">>).
    7: 
    8: -define(eq(Expected, Actual), ?assertEqual(Expected, Actual)).
    9: 
   10: %% Assertions
   11: 
   12: %% global config options
   13: -define(cfg(Key, Value, RawConfig), assert_option(Key, Value, parse(RawConfig))).
   14: -define(cfg(ExpectedOpts, RawConfig), assert_options(ExpectedOpts, parse(RawConfig))).
   15: 
   16: %% global config error
   17: -define(err(RawConfig), assert_error(parse(RawConfig))).
   18: 
   19: %% host-or-global config options
   20: -define(cfgh(KeyPrefix, Value, RawConfig),
   21:         assert_option_host_or_global(KeyPrefix, Value, RawConfig)).
   22: -define(cfgh(ExpectedOpts, RawConfig),
   23:         assert_options_host_or_global(ExpectedOpts, RawConfig)).
   24: 
   25: %% host-or-global config error
   26: -define(errh(RawConfig), assert_error_host_or_global(RawConfig)).
   27: 
   28: all() ->
   29:     [{group, file},
   30:      {group, general},
   31:      {group, listen},
   32:      {group, auth},
   33:      {group, pool},
   34:      {group, shaper_acl_access},
   35:      {group, s2s},
   36:      {group, modules},
   37:      {group, services},
   38:      {group, host_types_group}].
   39: 
   40: groups() ->
   41:     [{file, [parallel], [sample_pgsql,
   42:                          miscellaneous,
   43:                          s2s,
   44:                          modules,
   45:                          outgoing_pools]},
   46:      {host_types_group, [], [host_types_file,
   47:                              host_types_missing_auth_methods_and_modules,
   48:                              host_types_unsupported_modules,
   49:                              host_types_unsupported_auth_methods,
   50:                              host_types_unsupported_auth_methods_and_modules]},
   51:      {general, [parallel], [loglevel,
   52:                             hosts,
   53:                             host_types,
   54:                             default_server_domain,
   55:                             registration_timeout,
   56:                             language,
   57:                             all_metrics_are_global,
   58:                             sm_backend,
   59:                             max_fsm_queue,
   60:                             http_server_name,
   61:                             rdbms_server_type,
   62:                             route_subdomains,
   63:                             mongooseimctl_access_commands,
   64:                             routing_modules,
   65:                             replaced_wait_timeout,
   66:                             hide_service_name]},
   67:      {listen, [parallel], [listen_portip,
   68:                            listen_proto,
   69:                            listen_ip_version,
   70:                            listen_backlog,
   71:                            listen_proxy_protocol,
   72:                            listen_num_acceptors,
   73:                            listen_access,
   74:                            listen_shaper,
   75:                            listen_xml_socket,
   76:                            listen_zlib,
   77:                            listen_hibernate_after,
   78:                            listen_max_fsm_queue,
   79:                            listen_max_stanza_size,
   80:                            listen_tls_mode,
   81:                            listen_tls_module,
   82:                            listen_tls_verify,
   83:                            listen_tls_verify_mode,
   84:                            listen_tls_crl_files,
   85:                            listen_tls_certfile,
   86:                            listen_tls_cacertfile,
   87:                            listen_tls_dhfile,
   88:                            listen_tls_ciphers,
   89:                            listen_tls_versions,
   90:                            listen_tls_protocol_options,
   91:                            listen_check_from,
   92:                            listen_hidden_components,
   93:                            listen_conflict_behaviour,
   94:                            listen_password,
   95:                            listen_http_num_acceptors,
   96:                            listen_http_max_connections,
   97:                            listen_http_compress,
   98:                            listen_http_handlers,
   99:                            listen_http_handlers_websockets,
  100:                            listen_http_handlers_lasse,
  101:                            listen_http_handlers_static,
  102:                            listen_http_handlers_api,
  103:                            listen_http_handlers_domain]},
  104:      {auth, [parallel], [auth_methods,
  105:                          auth_password_format,
  106:                          auth_scram_iterations,
  107:                          auth_sasl_external,
  108:                          auth_allow_multiple_connections,
  109:                          auth_anonymous_protocol,
  110:                          auth_sasl_mechanisms,
  111:                          auth_ldap_pool,
  112:                          auth_ldap_bind_pool,
  113:                          auth_ldap_base,
  114:                          auth_ldap_uids,
  115:                          auth_ldap_filter,
  116:                          auth_ldap_dn_filter,
  117:                          auth_ldap_local_filter,
  118:                          auth_ldap_deref,
  119:                          auth_external,
  120:                          auth_http_basic_auth,
  121:                          auth_jwt,
  122:                          auth_riak_bucket_type,
  123:                          auth_rdbms_users_number_estimate,
  124:                          auth_dummy]},
  125:      {pool, [parallel], [pool_type,
  126:                          pool_tag,
  127:                          pool_scope,
  128:                          pool_workers,
  129:                          pool_strategy,
  130:                          pool_call_timeout,
  131:                          pool_rdbms_settings,
  132:                          pool_rdbms_keepalive_interval,
  133:                          pool_rdbms_server,
  134:                          pool_rdbms_port,
  135:                          pool_rdbms_tls,
  136:                          pool_http_host,
  137:                          pool_http_path_prefix,
  138:                          pool_http_request_timeout,
  139:                          pool_http_tls,
  140:                          pool_redis_host,
  141:                          pool_redis_port,
  142:                          pool_redis_database,
  143:                          pool_redis_password,
  144:                          pool_riak_address,
  145:                          pool_riak_port,
  146:                          pool_riak_credentials,
  147:                          pool_riak_cacertfile,
  148:                          pool_riak_tls,
  149:                          pool_cassandra_servers,
  150:                          pool_cassandra_keyspace,
  151:                          pool_cassandra_auth,
  152:                          pool_cassandra_tls,
  153:                          pool_ldap_host,
  154:                          pool_ldap_port,
  155:                          pool_ldap_servers,
  156:                          pool_ldap_encrypt,
  157:                          pool_ldap_rootdn,
  158:                          pool_ldap_password,
  159:                          pool_ldap_connect_interval,
  160:                          pool_ldap_tls]},
  161:      {shaper_acl_access, [parallel], [shaper,
  162:                                       acl,
  163:                                       access]},
  164:      {s2s, [parallel], [s2s_dns_timeout,
  165:                         s2s_dns_retries,
  166:                         s2s_outgoing_port,
  167:                         s2s_outgoing_ip_versions,
  168:                         s2s_outgoing_timeout,
  169:                         s2s_use_starttls,
  170:                         s2s_certfile,
  171:                         s2s_default_policy,
  172:                         s2s_host_policy,
  173:                         s2s_address,
  174:                         s2s_ciphers,
  175:                         s2s_domain_certfile,
  176:                         s2s_shared,
  177:                         s2s_max_retry_delay]},
  178:      {modules, [parallel], [mod_adhoc,
  179:                             mod_auth_token,
  180:                             mod_bosh,
  181:                             mod_caps,
  182:                             mod_cache_users,
  183:                             mod_carboncopy,
  184:                             mod_csi,
  185:                             mod_disco,
  186:                             mod_inbox,
  187:                             mod_global_distrib,
  188:                             mod_global_distrib_connections,
  189:                             mod_global_distrib_connections_endpoints,
  190:                             mod_global_distrib_connections_advertised_endpoints,
  191:                             mod_global_distrib_connections_tls,
  192:                             mod_global_distrib_redis,
  193:                             mod_global_distrib_cache,
  194:                             mod_global_distrib_bounce,
  195:                             mod_event_pusher_sns,
  196:                             mod_event_pusher_push,
  197:                             mod_event_pusher_http,
  198:                             mod_event_pusher_rabbit,
  199:                             mod_extdisco,
  200:                             mod_http_upload,
  201:                             mod_http_upload_s3,
  202:                             mod_jingle_sip,
  203:                             mod_keystore,
  204:                             mod_keystore_keys,
  205:                             mod_last,
  206:                             mod_mam_meta,
  207:                             mod_mam_meta_pm,
  208:                             mod_mam_meta_muc,
  209:                             mod_muc,
  210:                             mod_muc_default_room,
  211:                             mod_muc_default_room_affiliations,
  212:                             mod_muc_log,
  213:                             mod_muc_log_top_link,
  214:                             mod_muc_light,
  215:                             mod_muc_light_config_schema,
  216:                             mod_offline,
  217:                             mod_ping,
  218:                             mod_privacy,
  219:                             mod_private,
  220:                             mod_pubsub,
  221:                             mod_pubsub_pep_mapping,
  222:                             mod_pubsub_default_node_config,
  223:                             mod_push_service_mongoosepush,
  224:                             mod_register,
  225:                             mod_roster,
  226:                             mod_shared_roster_ldap,
  227:                             mod_sic,
  228:                             mod_stream_management,
  229:                             mod_stream_management_stale_h,
  230:                             mod_time,
  231:                             mod_vcard,
  232:                             mod_vcard_ldap_uids,
  233:                             mod_vcard_ldap_vcard_map,
  234:                             mod_vcard_ldap_search_fields,
  235:                             mod_vcard_ldap_search_reported,
  236:                             mod_version,
  237:                             modules_without_config]},
  238:      {services, [parallel], [service_admin_extra,
  239:                              service_mongoose_system_metrics]}
  240:     ].
  241: 
  242: init_per_suite(Config) ->
  243:     {ok, _} = application:ensure_all_started(jid),
  244:     create_files(Config),
  245:     Config.
  246: 
  247: end_per_suite(_Config) ->
  248:     ok.
  249: 
  250: init_per_group(host_types_group, Config) ->
  251:     Modules = [test_mim_module1, test_mim_module2, test_mim_module3],
  252:     AuthModules = [ejabberd_auth_test1, ejabberd_auth_test2, ejabberd_auth_test3],
  253:     [{mocked_modules, Modules ++ AuthModules} | Config];
  254: init_per_group(_, Config) ->
  255:     Config.
  256: 
  257: end_per_group(_, Config) ->
  258:     Config.
  259: 
  260: init_per_testcase(_, Config) ->
  261:     case proplists:get_value(mocked_modules, Config, no_mocks) of
  262:         no_mocks -> ok;
  263:         Modules -> [meck:new(M, [non_strict, no_link]) || M <- Modules]
  264:     end,
  265:     Config.
  266: 
  267: end_per_testcase(_, Config) ->
  268:     meck:unload(),
  269:     Config.
  270: 
  271: sample_pgsql(Config) ->
  272:     test_config_file(Config,  "mongooseim-pgsql").
  273: 
  274: miscellaneous(Config) ->
  275:     test_config_file(Config,  "miscellaneous").
  276: 
  277: s2s(Config) ->
  278:     test_config_file(Config,  "s2s_only").
  279: 
  280: modules(Config) ->
  281:     test_config_file(Config,  "modules").
  282: 
  283: outgoing_pools(Config) ->
  284:     test_config_file(Config,  "outgoing_pools").
  285: 
  286: host_types_file(Config) ->
  287:     Modules = [test_mim_module1, test_mim_module2,
  288:                ejabberd_auth_test1, ejabberd_auth_test2],
  289:     FN = fun() -> [dynamic_domains] end,
  290:     [meck:expect(M, supported_features, FN) || M <- Modules],
  291:     test_config_file(Config, "host_types").
  292: 
  293: host_types_missing_auth_methods_and_modules(Config) ->
  294:     Modules = [ejabberd_auth_test1, test_mim_module1, test_mim_module2],
  295:     [meck:unload(M) || M <- Modules],
  296:     {'EXIT', {{config_error, "Could not read the TOML configuration file", ErrorList}, _}}
  297:         = (catch test_config_file(Config, "host_types")),
  298:     MissingModules = [M || #{reason := module_not_found, module := M} <- ErrorList],
  299:     ?assertEqual(lists:sort(Modules), lists:usort(MissingModules)).
  300: 
  301: host_types_unsupported_modules(Config) ->
  302:     Modules = [ejabberd_auth_test1, ejabberd_auth_test2],
  303:     FN = fun() -> [dynamic_domains] end,
  304:     [meck:expect(M, supported_features, FN) || M <- Modules],
  305:     ?assertError({config_error, "Invalid host type configuration",
  306:                   %% please note that the sequence of these errors is not
  307:                   %% guarantied and may change in the future
  308:                   [#{reason := not_supported_module, module := test_mim_module1,
  309:                      host_type := <<"yet another host type">>},
  310:                    #{reason := not_supported_module, module := test_mim_module2,
  311:                      host_type := <<"another host type">>}]},
  312:                  test_config_file(Config, "host_types")).
  313: 
  314: host_types_unsupported_auth_methods(Config) ->
  315:     Modules = [test_mim_module1, test_mim_module2, ejabberd_auth_test1],
  316:     FN = fun() -> [dynamic_domains] end,
  317:     [meck:expect(M, supported_features, FN) || M <- Modules],
  318:     ?assertError({config_error, "Invalid host type configuration",
  319:                   %% please note that the sequence of these errors is not
  320:                   %% guarantied and may change in the future
  321:                   [#{reason := not_supported_auth_method, auth_method := test2,
  322:                      host_type := <<"yet another host type">>},
  323:                    #{reason := not_supported_auth_method, auth_method := test2,
  324:                      host_type := <<"some host type">>}]},
  325:                  test_config_file(Config, "host_types")).
  326: 
  327: host_types_unsupported_auth_methods_and_modules(Config) ->
  328:     Modules = [test_mim_module1, ejabberd_auth_test1],
  329:     FN = fun() -> [dynamic_domains] end,
  330:     [meck:expect(M, supported_features, FN) || M <- Modules],
  331:     ?assertError({config_error, "Invalid host type configuration",
  332:                   %% please note that the sequence of these errors is not
  333:                   %% guarantied and may change in the future
  334:                   [#{reason := not_supported_auth_method, auth_method := test2,
  335:                      host_type := <<"yet another host type">>},
  336:                    #{reason := not_supported_module, module := test_mim_module2,
  337:                      host_type := <<"another host type">>},
  338:                    #{reason := not_supported_auth_method, auth_method := test2,
  339:                      host_type := <<"some host type">>}]},
  340:                  test_config_file(Config, "host_types")).
  341: 
  342: %% tests: general
  343: loglevel(_Config) ->
  344:     ?cfg(loglevel, warning, #{}), % default
  345:     ?cfg(loglevel, debug, #{<<"general">> => #{<<"loglevel">> => <<"debug">>}}),
  346:     ?err(#{<<"general">> => #{<<"loglevel">> => <<"bebug">>}}),
  347:     %% make sure non-host options are not accepted in host_config
  348:     ?err(host_config(#{<<"general">> => #{<<"loglevel">> => <<"debug">>}})).
  349: 
  350: hosts(_Config) ->
  351:     ?cfg(hosts, [], % default
  352:          #{<<"general">> => #{<<"host_types">> => [<<"type1">>]}, without => [<<"hosts">>]}),
  353:     ?cfg(hosts, [<<"host1">>],
  354:          #{<<"general">> => #{<<"hosts">> => [<<"host1">>]}}),
  355:     ?cfg(hosts, [<<"host1">>, <<"host2">>],
  356:          #{<<"general">> => #{<<"hosts">> => [<<"host1">>, <<"host2">>]}}),
  357:     ?err(#{<<"general">> => #{<<"hosts">> => [<<"what is this?">>]}}),
  358:     ?err(#{<<"general">> => #{<<"hosts">> => [<<>>]}}),
  359:     ?err(#{<<"general">> => #{<<"hosts">> => [<<"host1">>, <<"host1">>]}}),
  360:     %% at least one host or host_type must be provided
  361:     ?err(#{<<"general">> => #{}, without => [<<"hosts">>]}),
  362:     ?err(#{<<"general">> => #{<<"hosts">> => []}}),
  363:     ?err(#{<<"general">> => #{<<"host_types">> => []}, without => [<<"hosts">>]}),
  364:     ?err(#{<<"general">> => #{<<"hosts">> => [], <<"host_types">> => []}}).
  365: 
  366: host_types(_Config) ->
  367:     ?cfg(host_types, [], #{}), % default
  368:     ?cfg([{host_types, [<<"type 1">>]},
  369:           {hosts, []}],
  370:          #{<<"general">> => #{<<"host_types">> => [<<"type 1">>]}, without => [<<"hosts">>]}),
  371:     ?cfg([{host_types, [<<"type 1">>, <<"type 2">>]},
  372:           {hosts, []}],
  373:          #{<<"general">> => #{<<"host_types">> => [<<"type 1">>, <<"type 2">>],
  374:                               <<"hosts">> => []}}),
  375:     ?err(#{<<"general">> => #{<<"host_types">> => [<<>>]}}),
  376:     ?err(#{<<"general">> => #{<<"host_types">> => [<<"type1">>, <<"type1">>]}}),
  377:     %% either hosts and host_types cannot have the same values
  378:     ?err(#{<<"general">> => #{<<"host_types">> => [<<"type1">>],
  379:                               <<"hosts">> => [<<"type1">>]}}).
  380: 
  381: default_server_domain(_Config) ->
  382:     ?cfg(default_server_domain, <<"host1">>,
  383:          #{<<"general">> => #{<<"default_server_domain">> => <<"host1">>}}),
  384:     ?err(#{<<"general">> => #{<<"default_server_domain">> => <<"what is this?">>}}),
  385:     ?err(#{<<"general">> => #{<<"default_server_domain">> => <<>>}}),
  386:     %% default_server_domain must be provided
  387:     ?err(#{without => [<<"default_server_domain">>]}).
  388: 
  389: registration_timeout(_Config) ->
  390:     ?cfg(registration_timeout, 600, #{}), % default
  391:     ?cfg(registration_timeout, infinity,
  392:          #{<<"general">> => #{<<"registration_timeout">> => <<"infinity">>}}),
  393:     ?cfg(registration_timeout, 300,
  394:          #{<<"general">> => #{<<"registration_timeout">> => 300}}),
  395:     ?err(#{<<"general">> => #{<<"registration_timeout">> => 0}}).
  396: 
  397: language(_Config) ->
  398:     ?cfg(language, <<"en">>, #{}), % default
  399:     ?cfg(language, <<"pl">>, #{<<"general">> => #{<<"language">> => <<"pl">>}}),
  400:     ?err(#{<<"general">> => #{<<"language">> => <<>>}}).
  401: 
  402: all_metrics_are_global(_Config) ->
  403:     ?cfg(all_metrics_are_global, false, #{}), % default
  404:     ?cfg(all_metrics_are_global, true, #{<<"general">> => #{<<"all_metrics_are_global">> => true}}),
  405:     ?err(#{<<"general">> => #{<<"all_metrics_are_global">> => <<"true">>}}).
  406: 
  407: sm_backend(_Config) ->
  408:     ?cfg(sm_backend, {mnesia, []}, #{}), % default
  409:     ?cfg(sm_backend, {mnesia, []}, #{<<"general">> => #{<<"sm_backend">> => <<"mnesia">>}}),
  410:     ?cfg(sm_backend, {redis, []}, #{<<"general">> => #{<<"sm_backend">> => <<"redis">>}}),
  411:     ?err(#{<<"general">> => #{<<"sm_backend">> => <<"amnesia">>}}).
  412: 
  413: max_fsm_queue(_Config) ->
  414:     ?cfg(max_fsm_queue, 100, #{<<"general">> => #{<<"max_fsm_queue">> => 100}}),
  415:     ?err(#{<<"general">> => #{<<"max_fsm_queue">> => -10}}).
  416: 
  417: http_server_name(_Config) ->
  418:     ?cfg(cowboy_server_name, "my server",
  419:          #{<<"general">> => #{<<"http_server_name">> => <<"my server">>}}),
  420:     ?err(#{<<"general">> => #{<<"http_server_name">> => #{}}}).
  421: 
  422: rdbms_server_type(_Config) ->
  423:     ?cfg(rdbms_server_type, generic, #{}), % default
  424:     ?cfg(rdbms_server_type, mssql, #{<<"general">> => #{<<"rdbms_server_type">> => <<"mssql">>}}),
  425:     ?cfg(rdbms_server_type, pgsql, #{<<"general">> => #{<<"rdbms_server_type">> => <<"pgsql">>}}),
  426:     ?err(#{<<"general">> => #{<<"rdbms_server_type">> => <<"nosql">>}}).
  427: 
  428: route_subdomains(_Config) ->
  429:     ?cfgh(route_subdomains, s2s, #{<<"general">> => #{<<"route_subdomains">> => <<"s2s">>}}),
  430:     ?errh(#{<<"general">> => #{<<"route_subdomains">> => <<"c2s">>}}).
  431: 
  432: mongooseimctl_access_commands(_Config) ->
  433:     ?cfg(mongooseimctl_access_commands, [], #{}), % default
  434:     AccessRule = #{<<"commands">> => [<<"join_cluster">>],
  435:                    <<"argument_restrictions">> => #{<<"node">> => <<"mim1@host1">>}},
  436:     ?cfg(mongooseimctl_access_commands, [{local, ["join_cluster"], [{node, "mim1@host1"}]}],
  437:          #{<<"general">> => #{<<"mongooseimctl_access_commands">> =>
  438:                                   #{<<"local">> => AccessRule}}}),
  439:     ?cfg(mongooseimctl_access_commands, [{local, all, [{node, "mim1@host1"}]}],
  440:          #{<<"general">> => #{<<"mongooseimctl_access_commands">> =>
  441:                                   #{<<"local">> => maps:remove(<<"commands">>, AccessRule)}}}),
  442:     ?cfg(mongooseimctl_access_commands, [{local, ["join_cluster"], []}],
  443:          #{<<"general">> => #{<<"mongooseimctl_access_commands">> =>
  444:                                   #{<<"local">> => maps:remove(<<"argument_restrictions">>,
  445:                                                                AccessRule)}}}),
  446:     ?cfg(mongooseimctl_access_commands, [{local, all, []}],
  447:          #{<<"general">> => #{<<"mongooseimctl_access_commands">> => #{<<"local">> => #{}}}}),
  448:     ?err(#{<<"general">> => #{<<"mongooseimctl_access_commands">> =>
  449:                                   #{<<"local">> => #{<<"commands">> => <<"all">>}}}}),
  450:     ?err(#{<<"general">> => #{<<"mongooseimctl_access_commands">> =>
  451:                                   #{<<"local">> => #{<<"argument_restrictions">> =>
  452:                                                          [<<"none">>]}}}}).
  453: 
  454: routing_modules(_Config) ->
  455:     ?cfg(routing_modules, ejabberd_router:default_routing_modules(), #{}), % default
  456:     ?cfg(routing_modules, [mongoose_router_global, mongoose_router_localdomain],
  457:          #{<<"general">> => #{<<"routing_modules">> => [<<"mongoose_router_global">>,
  458:                                                         <<"mongoose_router_localdomain">>]}}),
  459:     ?err(#{<<"general">> => #{<<"routing_modules">> => [<<"moongoose_router_global">>]}}).
  460: 
  461: replaced_wait_timeout(_Config) ->
  462:     ?cfg({replaced_wait_timeout, global}, 2000, #{}), % global default
  463:     ?cfgh(replaced_wait_timeout, 1000, #{<<"general">> => #{<<"replaced_wait_timeout">> => 1000}}),
  464:     ?errh(#{<<"general">> => #{<<"replaced_wait_timeout">> => 0}}).
  465: 
  466: hide_service_name(_Config) ->
  467:     ?cfg(hide_service_name, false, #{}), % default
  468:     ?cfg(hide_service_name, true, #{<<"general">> => #{<<"hide_service_name">> => true}}),
  469:     ?err(#{<<"general">> => #{<<"hide_service_name">> => []}}).
  470: 
  471: %% tests: listen
  472: 
  473: listen_portip(_Config) ->
  474:     ?cfg(listener_config(ejabberd_c2s, []), listen_raw(<<"c2s">>, #{})),
  475:     ?cfg(listen, [{{5222, {192, 168, 1, 16}, tcp}, ejabberd_c2s, []}],
  476:          listen_raw(<<"c2s">>, #{<<"ip_address">> => <<"192.168.1.16">>})),
  477:     ?cfg(listen, [{{5222, {8193, 3512, 3, 4, 5, 6, 7, 8}, tcp}, ejabberd_c2s, []}],
  478:          listen_raw(<<"c2s">>, #{<<"ip_address">> => <<"2001:db8:3:4:5:6:7:8">>})),
  479:     ?err(listen_raw(<<"c2s">>, #{<<"ip_address">> => <<"192.168.1.999">>})),
  480:     ?err(#{<<"listen">> => #{<<"c2s">> => [#{<<"ip_address">> => <<"192.168.1.16">>}]}}),
  481:     ?err(#{<<"listen">> => #{<<"c2s">> => [#{<<"port">> => <<"5222">>}]}}),
  482:     ?err(#{<<"listen">> => #{<<"c2s">> => [#{<<"port">> => 522222}]}}).
  483: 
  484: listen_proto(_Config) ->
  485:     ?cfg(listener_config(ejabberd_c2s, [{proto, tcp}]),
  486:          listen_raw(<<"c2s">>, #{<<"proto">> => <<"tcp">>})),
  487:     ?cfg(listen, [{{5222, {0, 0, 0, 0}, udp}, ejabberd_c2s, [{proto, udp}]}],
  488:          listen_raw(<<"c2s">>, #{<<"proto">> => <<"udp">>})),
  489:     ?err(listen_raw(<<"c2s">>, #{<<"proto">> => <<"pigeon">>})).
  490: 
  491: listen_ip_version(_Config) ->
  492:     ?cfg(listener_config(ejabberd_c2s, [inet]),
  493:          listen_raw(<<"c2s">>, #{<<"ip_version">> => 4})),
  494:     ?cfg(listen, [{{5222, {0, 0, 0, 0, 0, 0, 0, 0}, tcp}, ejabberd_c2s, []}],
  495:          listen_raw(<<"c2s">>, #{<<"ip_version">> => 6})),
  496:     ?err(listen_raw(<<"c2s">>, #{<<"ip_version">> => 7})).
  497: 
  498: listen_backlog(_Config) ->
  499:     ?cfg(listener_config(ejabberd_c2s, [{backlog, 10}]),
  500:          listen_raw(<<"c2s">>, #{<<"backlog">> => 10})),
  501:     ?err(listen_raw(<<"c2s">>, #{<<"backlog">> => -10})).
  502: 
  503: listen_proxy_protocol(_Config) ->
  504:     ?cfg(listener_config(ejabberd_c2s, [{proxy_protocol, true}]),
  505:          listen_raw(<<"c2s">>, #{<<"proxy_protocol">> => true})),
  506:     ?cfg(listener_config(ejabberd_s2s_in, [{proxy_protocol, true}]),
  507:          listen_raw(<<"s2s">>, #{<<"proxy_protocol">> => true})),
  508:     ?cfg(listener_config(ejabberd_service, [{proxy_protocol, true}]),
  509:          listen_raw(<<"service">>, #{<<"proxy_protocol">> => true})),
  510:     ?err(listen_raw(<<"c2s">>, #{<<"proxy_protocol">> => <<"awesome">>})).
  511: 
  512: listen_num_acceptors(_Config) ->
  513:     ?cfg(listener_config(ejabberd_c2s, [{acceptors_num, 100}]),
  514:          listen_raw(<<"c2s">>, #{<<"num_acceptors">> => 100})),
  515:     ?cfg(listener_config(ejabberd_s2s_in, [{acceptors_num, 100}]),
  516:          listen_raw(<<"s2s">>, #{<<"num_acceptors">> => 100})),
  517:     ?cfg(listener_config(ejabberd_service, [{acceptors_num, 100}]),
  518:          listen_raw(<<"service">>, #{<<"num_acceptors">> => 100})),
  519:     ?err(listen_raw(<<"c2s">>, #{<<"num_acceptors">> => 0})).
  520: 
  521: listen_access(_Config) ->
  522:     ?cfg(listener_config(ejabberd_c2s, [{access, rule1}]),
  523:          listen_raw(<<"c2s">>, #{<<"access">> => <<"rule1">>})),
  524:     ?cfg(listener_config(ejabberd_service, [{access, rule1}]),
  525:          listen_raw(<<"service">>, #{<<"access">> => <<"rule1">>})),
  526:     ?err(listen_raw(<<"c2s">>, #{<<"access">> => <<>>})).
  527: 
  528: listen_shaper(_Config) ->
  529:     ?cfg(listener_config(ejabberd_c2s, [{shaper, c2s_shaper}]),
  530:          listen_raw(<<"c2s">>, #{<<"shaper">> => <<"c2s_shaper">>})),
  531:     ?cfg(listener_config(ejabberd_s2s_in, [{shaper, s2s_shaper}]),
  532:          listen_raw(<<"s2s">>, #{<<"shaper">> => <<"s2s_shaper">>})),
  533:     ?cfg(listener_config(ejabberd_service, [{shaper_rule, fast}]),
  534:          listen_raw(<<"service">>, #{<<"shaper_rule">> => <<"fast">>})),
  535:     ?err(listen_raw(<<"s2s">>, #{<<"shaper">> => <<>>})).
  536: 
  537: listen_xml_socket(_Config) ->
  538:     ?cfg(listener_config(ejabberd_c2s, [{xml_socket, true}]),
  539:          listen_raw(<<"c2s">>, #{<<"xml_socket">> => true})),
  540:     ?err(listen_raw(<<"c2s">>, #{<<"xml_socket">> => 10})).
  541: 
  542: listen_zlib(_Config) ->
  543:     ?cfg(listener_config(ejabberd_c2s, [{zlib, 1024}]),
  544:          listen_raw(<<"c2s">>, #{<<"zlib">> => 1024})),
  545:     ?err(listen_raw(<<"c2s">>, #{<<"zlib">> => 0})).
  546: 
  547: listen_hibernate_after(_Config) ->
  548:     ?cfg(listener_config(ejabberd_c2s, [{hibernate_after, 10}]),
  549:          listen_raw(<<"c2s">>, #{<<"hibernate_after">> => 10})),
  550:     ?cfg(listener_config(ejabberd_s2s_in, [{hibernate_after, 10}]),
  551:          listen_raw(<<"s2s">>, #{<<"hibernate_after">> => 10})),
  552:     ?cfg(listener_config(ejabberd_service, [{hibernate_after, 10}]),
  553:          listen_raw(<<"service">>, #{<<"hibernate_after">> => 10})),
  554:     ?err(listen_raw(<<"c2s">>, #{<<"hibernate_after">> => -10})).
  555: 
  556: listen_max_stanza_size(_Config) ->
  557:     ?cfg(listener_config(ejabberd_c2s, [{max_stanza_size, 10000}]),
  558:          listen_raw(<<"c2s">>, #{<<"max_stanza_size">> => 10000})),
  559:     ?cfg(listener_config(ejabberd_s2s_in, [{max_stanza_size, 10000}]),
  560:          listen_raw(<<"s2s">>, #{<<"max_stanza_size">> => 10000})),
  561:     ?cfg(listener_config(ejabberd_service, [{max_stanza_size, 10000}]),
  562:          listen_raw(<<"service">>, #{<<"max_stanza_size">> => 10000})),
  563:     ?err(listen_raw(<<"c2s">>, #{<<"max_stanza_size">> => <<"infinity">>})).
  564: 
  565: listen_max_fsm_queue(_Config) ->
  566:     ?cfg(listener_config(ejabberd_c2s, [{max_fsm_queue, 1000}]),
  567:          listen_raw(<<"c2s">>, #{<<"max_fsm_queue">> => 1000})),
  568:     ?cfg(listener_config(ejabberd_service, [{max_fsm_queue, 1000}]),
  569:          listen_raw(<<"service">>, #{<<"max_fsm_queue">> => 1000})),
  570:     ?err(listen_raw(<<"s2s">>, #{<<"max_fsm_queue">> => 1000})), % only for c2s and service
  571:     ?err(listen_raw(<<"c2s">>, #{<<"max_fsm_queue">> => 0})).
  572: 
  573: listen_tls_mode(_Config) ->
  574:     ?cfg(listener_config(ejabberd_c2s, [starttls]),
  575:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"mode">> => <<"starttls">>}})),
  576:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"mode">> => <<"stoptls">>}})).
  577: 
  578: listen_tls_module(_Config) ->
  579:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls}]),
  580:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>}})),
  581:     ?cfg(listener_config(ejabberd_c2s, []),
  582:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"fast_tls">>}})),
  583:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"slow_tls">>}})).
  584: 
  585: listen_tls_verify(_Config) ->
  586:     ?cfg(listener_config(ejabberd_c2s, [verify_peer]),
  587:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"verify_peer">> => true}})),
  588:     ?cfg(listener_config(ejabberd_c2s, [verify_none]),
  589:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"verify_peer">> => false}})),
  590:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls}, verify_peer]),
  591:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  592:                                                 <<"verify_peer">> => true}})),
  593:     ?cfg(listener_config(ejabberd_cowboy, [{ssl, [{verify, verify_peer}]}]),
  594:          listen_raw(<<"http">>, #{<<"tls">> => #{<<"verify_peer">> => true}})),
  595:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls}, verify_none]),
  596:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  597:                                                 <<"verify_peer">> => false}})),
  598:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"verify_peer">> => <<"maybe">>}})).
  599: 
  600: listen_tls_verify_mode(_Config) ->
  601:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls},
  602:                                         {ssl_options, [{verify_fun, {peer, true}}]}]),
  603:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  604:                                                 <<"verify_mode">> => <<"peer">>}})),
  605:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls},
  606:                                         {ssl_options, [{verify_fun, {selfsigned_peer, false}}]}]),
  607:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  608:                                                 <<"verify_mode">> => <<"selfsigned_peer">>,
  609:                                                 <<"disconnect_on_failure">> => false}})),
  610:     ?cfg(listener_config(ejabberd_cowboy, [{ssl, [{verify_mode, peer}]}]),
  611:          listen_raw(<<"http">>, #{<<"tls">> => #{<<"verify_mode">> => <<"peer">>}})),
  612:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  613:                                                 <<"verify_mode">> => <<"peer">>,
  614:                                                 <<"disconnect_on_failure">> => <<"false">>}})),
  615:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  616:                                                 <<"verify_mode">> => <<"whatever">>}})),
  617:     ?err(listen_raw(<<"http">>, #{<<"tls">> => #{<<"verify_mode">> => <<"whatever">>}})).
  618: 
  619: listen_tls_crl_files(_Config) ->
  620:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls},
  621:                                         {crlfiles, ["file1", "file2"]}]),
  622:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  623:                                                 <<"crl_files">> => [<<"file1">>,
  624:                                                                     <<"file2">>]}})),
  625:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  626:                                                 <<"crl_files">> => [<<>>]}})),
  627:     %% only for just_tls
  628:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"crl_files">> => [<<"file1">>,
  629:                                                                     <<"file2">>]}})).
  630: 
  631: listen_tls_certfile(_Config) ->
  632:     ?cfg(listener_config(ejabberd_c2s, [{certfile, "mycert.pem"}]),
  633:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"certfile">> => <<"mycert.pem">>}})),
  634:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls},
  635:                                         {ssl_options, [{certfile, "mycert.pem"}]}]),
  636:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  637:                                                 <<"certfile">> => <<"mycert.pem">>}})),
  638:     ?cfg(listener_config(ejabberd_cowboy, [{ssl, [{certfile, "mycert.pem"}]}]),
  639:          listen_raw(<<"http">>, #{<<"tls">> => #{<<"certfile">> => <<"mycert.pem">>}})),
  640:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"certfile">> => <<>>}})).
  641: 
  642: listen_tls_cacertfile(_Config) ->
  643:     ?cfg(listener_config(ejabberd_c2s, [{cafile, "cacert.pem"}]),
  644:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"cacertfile">> => <<"cacert.pem">>}})),
  645:     ?cfg(listener_config(ejabberd_s2s_in, [{cafile, "cacert.pem"}]),
  646:          listen_raw(<<"s2s">>, #{<<"tls">> => #{<<"cacertfile">> => <<"cacert.pem">>}})),
  647:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls},
  648:                                         {ssl_options, [{cacertfile, "cacert.pem"}]}]),
  649:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  650:                                                 <<"cacertfile">> => <<"cacert.pem">>}})),
  651:     ?cfg(listener_config(ejabberd_cowboy, [{ssl, [{cacertfile, "cacert.pem"}]}]),
  652:          listen_raw(<<"http">>, #{<<"tls">> => #{<<"cacertfile">> => <<"cacert.pem">>}})),
  653:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"cacertfile">> => <<>>}})).
  654: 
  655: listen_tls_dhfile(_Config) ->
  656:     ?cfg(listener_config(ejabberd_c2s, [{dhfile, "dh.pem"}]),
  657:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"dhfile">> => <<"dh.pem">>}})),
  658:     ?cfg(listener_config(ejabberd_s2s_in, [{dhfile, "dh.pem"}]),
  659:          listen_raw(<<"s2s">>, #{<<"tls">> => #{<<"dhfile">> => <<"dh.pem">>}})),
  660:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls},
  661:                                         {ssl_options, [{dhfile, "dh.pem"}]}]),
  662:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  663:                                                 <<"dhfile">> => <<"dh.pem">>}})),
  664:     ?cfg(listener_config(ejabberd_cowboy, [{ssl, [{dhfile, "dh.pem"}]}]),
  665:          listen_raw(<<"http">>, #{<<"tls">> => #{<<"dhfile">> => <<"dh.pem">>}})),
  666:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"dhfile">> => <<>>}})).
  667: 
  668: listen_tls_ciphers(_Config) ->
  669:     ?cfg(listener_config(ejabberd_c2s, [{ciphers, "TLS_AES_256_GCM_SHA384"}]),
  670:          listen_raw(<<"c2s">>,
  671:                     #{<<"tls">> => #{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>}})),
  672:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls},
  673:                                         {ssl_options, [{ciphers, "TLS_AES_256_GCM_SHA384"}]}]),
  674:          listen_raw(<<"c2s">>,
  675:                     #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  676:                                      <<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>}})),
  677:     ?cfg(listener_config(ejabberd_s2s_in, [{ciphers, "TLS_AES_256_GCM_SHA384"}]),
  678:          listen_raw(<<"s2s">>,
  679:                     #{<<"tls">> => #{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>}})),
  680:     ?cfg(listener_config(ejabberd_cowboy, [{ssl, [{ciphers, "TLS_AES_256_GCM_SHA384"}]}]),
  681:          listen_raw(<<"http">>,
  682:                     #{<<"tls">> => #{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>}})),
  683:     ?err(listen_raw(<<"c2s">>,
  684:                     #{<<"tls">> => #{<<"ciphers">> => [<<"TLS_AES_256_GCM_SHA384">>]}})).
  685: 
  686: listen_tls_versions(_Config) ->
  687:     ?cfg(listener_config(ejabberd_c2s, [{tls_module, just_tls},
  688:                                         {ssl_options, [{versions, ['tlsv1.2', 'tlsv1.3']}]}]),
  689:          listen_raw(<<"c2s">>,
  690:                     #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  691:                                      <<"versions">> => [<<"tlsv1.2">>, <<"tlsv1.3">>]}})),
  692:     ?err(listen_raw(<<"c2s">>,
  693:                     #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  694:                                      <<"versions">> => <<"tlsv1.2">>}})).
  695: 
  696: listen_tls_protocol_options(_Config) ->
  697:     ?cfg(listener_config(ejabberd_c2s, [{protocol_options, ["nosslv2"]}]),
  698:          listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"protocol_options">> => [<<"nosslv2">>]}})),
  699:     ?cfg(listener_config(ejabberd_s2s_in, [{protocol_options, ["nosslv2"]}]),
  700:          listen_raw(<<"s2s">>, #{<<"tls">> => #{<<"protocol_options">> => [<<"nosslv2">>]}})),
  701:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"protocol_options">> => [<<>>]}})),
  702:     ?err(listen_raw(<<"s2s">>, #{<<"tls">> => #{<<"protocol_options">> => [<<>>]}})),
  703:     ?err(listen_raw(<<"c2s">>, #{<<"tls">> => #{<<"module">> => <<"just_tls">>,
  704:                                                 <<"protocol_options">> => [<<"nosslv2">>]}})).
  705: 
  706: listen_check_from(_Config) ->
  707:     ?cfg(listener_config(ejabberd_service, [{service_check_from, false}]),
  708:          listen_raw(<<"service">>, #{<<"check_from">> => false})),
  709:     ?err(listen_raw(<<"service">>, #{<<"check_from">> => 1})).
  710: 
  711: listen_hidden_components(_Config) ->
  712:     ?cfg(listener_config(ejabberd_service, [{hidden_components, true}]),
  713:          listen_raw(<<"service">>, #{<<"hidden_components">> => true})),
  714:     ?err(listen_raw(<<"service">>, #{<<"hidden_components">> => <<"yes">>})).
  715: 
  716: listen_conflict_behaviour(_Config) ->
  717:     ?cfg(listener_config(ejabberd_service, [{conflict_behaviour, kick_old}]),
  718:          listen_raw(<<"service">>, #{<<"conflict_behaviour">> => <<"kick_old">>})),
  719:     ?err(listen_raw(<<"service">>, #{<<"conflict_behaviour">> => <<"kill_server">>})).
  720: 
  721: listen_password(_Config) ->
  722:     ?cfg(listener_config(ejabberd_service, [{password, "secret"}]),
  723:          listen_raw(<<"service">>, #{<<"password">> => <<"secret">>})),
  724:     ?err(listen_raw(<<"service">>, #{<<"password">> => <<>>})).
  725: 
  726: listen_http_num_acceptors(_Config) ->
  727:     ?cfg(listener_config(ejabberd_cowboy, [{transport_options, [{num_acceptors, 10}]}]),
  728:          listen_raw(<<"http">>, #{<<"transport">> => #{<<"num_acceptors">> => 10}})),
  729:     ?err(listen_raw(<<"http">>, #{<<"transport">> => #{<<"num_acceptors">> => 0}})).
  730: 
  731: listen_http_max_connections(_Config) ->
  732:     ?cfg(listener_config(ejabberd_cowboy, [{transport_options, [{max_connections, 100}]}]),
  733:          listen_raw(<<"http">>, #{<<"transport">> => #{<<"max_connections">> => 100}})),
  734:     ?cfg(listener_config(ejabberd_cowboy, [{transport_options, [{max_connections, infinity}]}]),
  735:          listen_raw(<<"http">>, #{<<"transport">> =>
  736:                                       #{<<"max_connections">> => <<"infinity">>}})),
  737:     ?err(listen_raw(<<"http">>, #{<<"transport">> => #{<<"max_connections">> => -1}})).
  738: 
  739: listen_http_compress(_Config) ->
  740:     ?cfg(listener_config(ejabberd_cowboy, [{protocol_options, [{compress, true}]}]),
  741:          listen_raw(<<"http">>, #{<<"protocol">> => #{<<"compress">> => true}})),
  742:     ?err(listen_raw(<<"http">>, #{<<"protocol">> => #{<<"compress">> => 0}})).
  743: 
  744: listen_http_handlers(_Config) ->
  745:     ?cfg(listener_config(ejabberd_cowboy, [{modules, [{"_", "/http-bind", mod_bosh, []}]}]),
  746:          listen_raw(<<"http">>, #{<<"handlers">> =>
  747:                                       #{<<"mod_bosh">> =>
  748:                                             [#{<<"host">> => <<"_">>,
  749:                                                <<"path">> => <<"/http-bind">>}]}})),
  750:     ?err(listen_raw(<<"http">>, #{<<"handlers">> =>
  751:                                       #{<<"mod_bosch">> =>
  752:                                             [#{<<"host">> => <<"dishwasher">>,
  753:                                                <<"path">> => <<"/cutlery">>}]}})),
  754:     ?err(listen_raw(<<"http">>, #{<<"handlers">> =>
  755:                                       #{<<"mod_bosh">> =>
  756:                                             [#{<<"host">> => <<"pathless">>}]}})),
  757:     ?err(listen_raw(<<"http">>, #{<<"handlers">> =>
  758:                                       #{<<"mod_bosh">> =>
  759:                                             [#{<<"host">> => <<>>,
  760:                                                <<"path">> => <<"/">>}]}})),
  761:     ?err(listen_raw(<<"http">>, #{<<"handlers">> =>
  762:                                       #{<<"mod_bosh">> =>
  763:                                             [#{<<"path">> => <<"hostless">>}]}})).
  764: 
  765: listen_http_handlers_websockets(_Config) ->
  766:     ?cfg(listener_config(ejabberd_cowboy, [{modules, [{"localhost", "/api", mod_websockets, []}]}]),
  767:          http_handler_raw(<<"mod_websockets">>, #{})),
  768:     ?cfg(listener_config(ejabberd_cowboy, [{modules, [{"localhost", "/api", mod_websockets,
  769:                                                        [{ejabberd_service, [{access, all}]}]
  770:                                                       }]}]),
  771:          http_handler_raw(<<"mod_websockets">>, #{<<"service">> => #{<<"access">> => <<"all">>}})),
  772:     ?err(http_handler_raw(<<"mod_websockets">>, #{<<"service">> => <<"unbelievable">>})).
  773: 
  774: listen_http_handlers_lasse(_Config) ->
  775:     ?cfg(listener_config(ejabberd_cowboy, [{modules, [{"localhost", "/api", lasse_handler,
  776:                                                        [mongoose_client_api_sse]
  777:                                                       }]}]),
  778:          http_handler_raw(<<"lasse_handler">>, #{<<"module">> => <<"mongoose_client_api_sse">>})),
  779:     ?err(http_handler_raw(<<"lasse_handler">>, #{<<"module">> => <<"mooongooose_api_ssie">>})),
  780:     ?err(http_handler_raw(<<"lasse_handler">>, #{})).
  781: 
  782: listen_http_handlers_static(_Config) ->
  783:     ?cfg(listener_config(ejabberd_cowboy, [{modules, [{"localhost", "/api", cowboy_static,
  784:                                                        {priv_dir, cowboy_swagger, "swagger",
  785:                                                         [{mimetypes, cow_mimetypes, all}]}
  786:                                                       }]}]),
  787:          http_handler_raw(<<"cowboy_static">>, #{<<"type">> => <<"priv_dir">>,
  788:                                                  <<"app">> => <<"cowboy_swagger">>,
  789:                                                  <<"content_path">> => <<"swagger">>})),
  790:     ?err(http_handler_raw(<<"cowboy_static">>, #{<<"type">> => <<"priv_dir">>,
  791:                                                  <<"app">> => <<"cowboy_swagger">>})).
  792: 
  793: listen_http_handlers_api(_Config) ->
  794:     ?cfg(listener_config(ejabberd_cowboy, [{modules, [{"localhost", "/api", mongoose_api,
  795:                                                        [{handlers, [mongoose_api_metrics,
  796:                                                                     mongoose_api_users]}]}
  797:                                                      ]}]),
  798:          http_handler_raw(<<"mongoose_api">>, #{<<"handlers">> => [<<"mongoose_api_metrics">>,
  799:                                                                    <<"mongoose_api_users">>]})),
  800:     ?err(http_handler_raw(<<"mongoose_api">>, #{<<"handlers">> => [<<"not_an_api_module">>]})),
  801:     ?err(http_handler_raw(<<"mongoose_api">>, #{})).
  802: 
  803: listen_http_handlers_domain(_Config) ->
  804:     ?cfg(listener_config(ejabberd_cowboy,
  805:                          [{modules, [{"localhost", "/api", mongoose_domain_handler,
  806:                                       [{password, <<"cool">>}, {username, <<"admin">>}]
  807:                                      }]}]),
  808:          http_handler_raw(<<"mongoose_domain_handler">>,
  809:                           #{<<"username">> => <<"admin">>, <<"password">> => <<"cool">>})),
  810:     ?cfg(listener_config(ejabberd_cowboy,
  811:                          [{modules, [{"localhost", "/api", mongoose_domain_handler,
  812:                                       [] }]}]),
  813:          http_handler_raw(<<"mongoose_domain_handler">>, #{})),
  814:     %% Both username and password required. Or none.
  815:     ?err(http_handler_raw(<<"mongoose_domain_handler">>, #{<<"username">> => <<"admin">>})),
  816:     ?err(http_handler_raw(<<"mongoose_domain_handler">>, #{<<"password">> => <<"cool">>})).
  817: 
  818: %% tests: auth
  819: 
  820: auth_methods(_Config) ->
  821:     ?cfgh(auth, #{methods => [internal, rdbms]},
  822:           #{<<"auth">> => #{<<"methods">> => [<<"internal">>, <<"rdbms">>]}}),
  823:     ?errh(#{<<"auth">> => #{<<"methods">> => [<<"supernatural">>]}}).
  824: 
  825: auth_password_format(_Config) ->
  826:     ?cfgh(auth, #{password_format => {scram, [sha, sha256]}},
  827:           #{<<"auth">> => #{<<"password">> => #{<<"format">> => <<"scram">>,
  828:                                                 <<"hash">> => [<<"sha">>, <<"sha256">>]}}}),
  829:     ?cfgh(auth, #{password_format => scram},
  830:           #{<<"auth">> => #{<<"password">> => #{<<"format">> => <<"scram">>}}}),
  831:     ?cfgh(auth, #{password_format => plain},
  832:           #{<<"auth">> => #{<<"password">> => #{<<"format">> => <<"plain">>}}}),
  833:     ?errh(#{<<"auth">> => #{<<"password">> => #{<<"format">> => <<"no password">>}}}),
  834:     ?errh(#{<<"auth">> => #{<<"password">> => #{<<"format">> => <<"scram">>,
  835:                                                 <<"hash">> => []}}}),
  836:     ?errh(#{<<"auth">> => #{<<"password">> => #{<<"format">> => <<"scram">>,
  837:                                                 <<"hash">> => [<<"sha1234">>]}}}).
  838: 
  839: auth_scram_iterations(_Config) ->
  840:     ?cfgh(auth, #{scram_iterations => 1000},
  841:           #{<<"auth">> => #{<<"scram_iterations">> => 1000}}),
  842:     ?errh(#{<<"auth">> => #{<<"scram_iterations">> => false}}).
  843: 
  844: auth_sasl_external(_Config) ->
  845:     ?cfgh(auth, #{sasl_external => [standard,
  846:                                     common_name,
  847:                                     {mod, cyrsasl_external_verification}]},
  848:           #{<<"auth">> => #{<<"sasl_external">> =>
  849:                                 [<<"standard">>,
  850:                                  <<"common_name">>,
  851:                                  <<"cyrsasl_external_verification">>]}}),
  852:     ?errh(#{<<"auth">> => #{<<"sasl_external">> => [<<"unknown">>]}}).
  853: 
  854: auth_sasl_mechanisms(_Config) ->
  855:     ?cfgh(auth, #{sasl_mechanisms => [cyrsasl_external, cyrsasl_scram]},
  856:           #{<<"auth">> => #{<<"sasl_mechanisms">> => [<<"external">>, <<"scram">>]}}),
  857:     ?errh(#{<<"auth">> => #{<<"sasl_mechanisms">> => [<<"none">>]}}).
  858: 
  859: auth_allow_multiple_connections(_Config) ->
  860:     ?cfgh(auth, #{anonymous => #{allow_multiple_connections => true}},
  861:           auth_raw(<<"anonymous">>, #{<<"allow_multiple_connections">> => true})),
  862:     ?errh(auth_raw(<<"anonymous">>, #{<<"allow_multiple_connections">> => <<"yes">>})).
  863: 
  864: auth_anonymous_protocol(_Config) ->
  865:     ?cfgh(auth, #{anonymous => #{protocol => login_anon}},
  866:           auth_raw(<<"anonymous">>, #{<<"protocol">> => <<"login_anon">>})),
  867:     ?errh(auth_raw(<<"anonymous">>, #{<<"protocol">> => <<"none">>})).
  868: 
  869: auth_ldap_pool(_Config) ->
  870:     ?cfgh(auth, #{ldap => #{pool_tag => ldap_pool}},
  871:           auth_ldap_raw(#{<<"pool_tag">> => <<"ldap_pool">>})),
  872:     ?errh(auth_ldap_raw(#{<<"pool_tag">> => <<>>})).
  873: 
  874: auth_ldap_bind_pool(_Config) ->
  875:     ?cfgh(auth, #{ldap => #{bind_pool_tag => ldap_bind_pool}},
  876:           auth_ldap_raw(#{<<"bind_pool_tag">> => <<"ldap_bind_pool">>})),
  877:     ?errh(auth_ldap_raw(#{<<"bind_pool_tag">> => true})).
  878: 
  879: auth_ldap_base(_Config) ->
  880:     ?cfgh(auth, #{ldap => #{base => <<"ou=Users,dc=example,dc=com">>}},
  881:           auth_ldap_raw(#{<<"base">> => <<"ou=Users,dc=example,dc=com">>})),
  882:     ?errh(auth_ldap_raw(#{<<"base">> => 10})).
  883: 
  884: auth_ldap_uids(_Config) ->
  885:     ?cfgh(auth, #{ldap => #{uids => [{<<"uid1">>, <<"user=%u">>}]}},
  886:           auth_ldap_raw(#{<<"uids">> => [#{<<"attr">> => <<"uid1">>,
  887:                                            <<"format">> => <<"user=%u">>}]})),
  888:     ?cfgh(auth, #{ldap => #{uids => [<<"uid1">>]}},
  889:           auth_ldap_raw(#{<<"uids">> => [#{<<"attr">> => <<"uid1">>}]})),
  890:     ?errh(auth_ldap_raw(#{<<"uids">> => [#{<<"format">> => <<"user=%u">>}]})).
  891: 
  892: auth_ldap_filter(_Config) ->
  893:     ?cfgh(auth, #{ldap => #{filter => <<"(objectClass=inetOrgPerson)">>}},
  894:           auth_ldap_raw(#{<<"filter">> => <<"(objectClass=inetOrgPerson)">>})),
  895:     ?errh(auth_ldap_raw(#{<<"filter">> => 10})).
  896: 
  897: auth_ldap_dn_filter(_Config) ->
  898:     ?cfgh(auth, #{ldap => #{dn_filter => {<<"(user=%u@%d)">>, []}}},
  899:           auth_ldap_raw(#{<<"dn_filter">> => #{<<"filter">> => <<"(user=%u@%d)">>}})),
  900:     Pattern = <<"(&(name=%s)(owner=%D)(user=%u@%d))">>,
  901:     ?cfgh(auth, #{ldap => #{dn_filter => {Pattern, [<<"sn">>]}}},
  902:           auth_ldap_raw(#{<<"dn_filter">> => #{<<"filter">> => Pattern,
  903:                                                <<"attributes">> => [<<"sn">>]}})),
  904:     ?errh(auth_ldap_raw(#{<<"dn_filter">> => #{<<"attributes">> => [<<"sn">>]}})),
  905:     ?errh(auth_ldap_raw(#{<<"dn_filter">> => #{<<"filter">> => 12}})),
  906:     ?errh(auth_ldap_raw(#{<<"dn_filter">> => #{<<"filter">> => Pattern,
  907:                                                <<"attributes">> => <<"sn">>}})).
  908: 
  909: auth_ldap_local_filter(_Config) ->
  910:     Filter = #{<<"operation">> => <<"equal">>,
  911:                <<"attribute">> => <<"accountStatus">>,
  912:                <<"values">> => [<<"enabled">>]},
  913:     ?cfgh(auth, #{ldap => #{local_filter => {equal, {"accountStatus", ["enabled"]}}}},
  914:           auth_ldap_raw(#{<<"local_filter">> => Filter})),
  915:     [?errh(auth_ldap_raw(#{<<"local_filter">> => maps:remove(K, Filter)})) ||
  916:         K <- maps:keys(Filter)],
  917:     ?errh(auth_ldap_raw(#{<<"local_filter">> => Filter#{<<"operation">> := <<"lt">>}})),
  918:     ?errh(auth_ldap_raw(#{<<"local_filter">> => Filter#{<<"attribute">> := <<>>}})),
  919:     ?errh(auth_ldap_raw(#{<<"local_filter">> => Filter#{<<"values">> := []}})).
  920: 
  921: auth_ldap_deref(_Config) ->
  922:     ?cfgh(auth, #{ldap => #{deref => always}}, auth_ldap_raw(#{<<"deref">> => <<"always">>})),
  923:     ?errh(auth_ldap_raw(#{<<"deref">> => <<"sometimes">>})).
  924: 
  925: auth_external(_Config) ->
  926:     RequiredOpts = #{<<"program">> => <<"/usr/bin/auth">>},
  927:     Config = #{program => "/usr/bin/auth"},
  928:     ?cfgh(auth, #{external => Config},
  929:           auth_raw(<<"external">>, RequiredOpts)),
  930:     ?cfgh(auth, #{external => Config#{instances => 2}},
  931:           auth_raw(<<"external">>, RequiredOpts#{<<"instances">> => 2})),
  932:     ?errh(auth_raw(<<"external">>, #{<<"program">> => <<>>})),
  933:     ?errh(auth_raw(<<"external">>, #{<<"instances">> => 2})),
  934:     ?errh(auth_raw(<<"external">>, RequiredOpts#{<<"instances">> => 0})).
  935: 
  936: auth_http_basic_auth(_Config) ->
  937:     ?cfgh(auth, #{http => #{basic_auth => "admin:admin123"}},
  938:           auth_raw(<<"http">>, #{<<"basic_auth">> => <<"admin:admin123">>})),
  939:     ?errh(auth_raw(<<"http">>, #{<<"basic_auth">> => true})).
  940: 
  941: auth_jwt(_Config) ->
  942:     Opts = #{<<"secret">> => #{<<"value">> => <<"secret123">>},
  943:              <<"algorithm">> => <<"HS512">>,
  944:              <<"username_key">> => <<"user">>}, % tested together as all options are required
  945:     Config = #{algorithm => <<"HS512">>,
  946:                secret => {value, "secret123"},
  947:                username_key => user},
  948:     ?cfgh(auth, #{jwt => Config},
  949:           auth_raw(<<"jwt">>, Opts)),
  950:     ?cfgh(auth, #{jwt => Config#{secret => {file, "/home/user/jwt_secret"}}},
  951:           auth_raw(<<"jwt">>, Opts#{<<"secret">> := #{<<"file">> => <<"/home/user/jwt_secret">>}})),
  952:     ?cfgh(auth, #{jwt => Config#{secret => {env, "SECRET"}}},
  953:           auth_raw(<<"jwt">>, Opts#{<<"secret">> := #{<<"env">> => <<"SECRET">>}})),
  954:     ?errh(auth_raw(<<"jwt">>, Opts#{<<"secret">> := #{<<"value">> => 123}})),
  955:     ?errh(auth_raw(<<"jwt">>, Opts#{<<"secret">> := #{<<"file">> => <<>>}})),
  956:     ?errh(auth_raw(<<"jwt">>, Opts#{<<"secret">> := #{<<"env">> => <<>>}})),
  957:     ?errh(auth_raw(<<"jwt">>, Opts#{<<"secret">> := #{<<"file">> => <<"/jwt_secret">>,
  958:                                                       <<"env">> => <<"SECRET">>}})),
  959:     ?errh(auth_raw(<<"jwt">>, Opts#{<<"algorithm">> := <<"bruteforce">>})),
  960:     ?errh(auth_raw(<<"jwt">>, Opts#{<<"username_key">> := <<>>})),
  961:     [?errh(auth_raw(<<"jwt">>, maps:without([K], Opts))) || K <- maps:keys(Opts)].
  962: 
  963: auth_riak_bucket_type(_Config) ->
  964:     ?cfgh(auth, #{riak => #{bucket_type => <<"buckethead">>}},
  965:           auth_raw(<<"riak">>, #{<<"bucket_type">> => <<"buckethead">>})),
  966:     ?errh(auth_raw(<<"riak">>, #{<<"bucket_type">> => <<>>})).
  967: 
  968: auth_rdbms_users_number_estimate(_Config) ->
  969:     ?cfgh(auth, #{rdbms => #{users_number_estimate => true}},
  970:           auth_raw(<<"rdbms">>, #{<<"users_number_estimate">> => true})),
  971:     ?errh(auth_raw(<<"rdbms">>, #{<<"users_number_estimate">> => 1200})).
  972: 
  973: auth_dummy(_Config) ->
  974:     ?cfgh(auth, #{dummy => #{base_time => 0}},
  975:           auth_raw(<<"dummy">>, #{<<"base_time">> => 0})),
  976:     ?cfgh(auth, #{dummy => #{variance => 10}},
  977:           auth_raw(<<"dummy">>, #{<<"variance">> => 10})),
  978:     ?errh(auth_raw(<<"dummy">>, #{<<"base_time">> => -5})),
  979:     ?errh(auth_raw(<<"dummy">>, #{<<"variance">> => 0})).
  980: 
  981: %% tests: outgoing_pools
  982: 
  983: pool_type(_Config) ->
  984:     ?cfg(pool_config({http, global, default, [], []}),
  985:          pool_raw(<<"http">>, <<"default">>, #{})),
  986:     ?err(pool_raw(<<"swimming_pool">>, <<"default">>, #{})).
  987: 
  988: pool_tag(_Config) ->
  989:     ?cfg(pool_config({http, global, my_pool, [], []}),
  990:          pool_raw(<<"http">>, <<"my_pool">>, #{})),
  991:     ?err(pool_raw(<<"http">>, 1000, #{})).
  992: 
  993: pool_scope(_Config) ->
  994:     ?cfg(pool_config({http, global, default, [], []}),
  995:          pool_raw(<<"http">>, <<"default">>, #{})),
  996:     ?cfg(pool_config({http, global, default, [], []}),
  997:          pool_raw(<<"http">>, <<"default">>, #{<<"scope">> => <<"global">>})),
  998:     ?cfg(pool_config({http, host, default, [], []}),
  999:          pool_raw(<<"http">>, <<"default">>, #{<<"scope">> => <<"host">>})),
 1000:     ?cfg(pool_config({http, <<"localhost">>, default, [], []}),
 1001:          pool_raw(<<"http">>, <<"default">>, #{<<"scope">> => <<"single_host">>,
 1002:                                                <<"host">> => <<"localhost">>})),
 1003:     ?err(pool_raw(<<"http">>, <<"default">>, #{<<"scope">> => <<"whatever">>})),
 1004:     ?err(pool_raw(<<"http">>, <<"default">>, #{<<"scope">> => <<"single_host">>})).
 1005: 
 1006: pool_workers(_Config) ->
 1007:     ?cfg(pool_config({http, global, default, [{workers, 10}], []}),
 1008:          pool_raw(<<"http">>, <<"default">>, #{<<"workers">> => 10})),
 1009:     ?err(pool_raw(<<"http">>, <<"default">>, #{<<"workers">> => 0})).
 1010: 
 1011: pool_strategy(_Config) ->
 1012:     ?cfg(pool_config({http, global, default, [{strategy, best_worker}], []}),
 1013:          pool_raw(<<"http">>, <<"default">>, #{<<"strategy">> => <<"best_worker">>})),
 1014:     ?err(pool_raw(<<"http">>, <<"default">>, #{<<"strategy">> => <<"worst_worker">>})).
 1015: 
 1016: pool_call_timeout(_Config) ->
 1017:     ?cfg(pool_config({http, global, default, [{call_timeout, 5000}], []}),
 1018:          pool_raw(<<"http">>, <<"default">>, #{<<"call_timeout">> => 5000})),
 1019:     ?err(pool_raw(<<"http">>, <<"default">>, #{<<"call_timeout">> => 0})).
 1020: 
 1021: pool_rdbms_settings(_Config) ->
 1022:     ?cfg(pool_config({rdbms, global, default, [], [{server, "DSN=mydb"}]}),
 1023:          pool_conn_raw(<<"rdbms">>, #{<<"driver">> => <<"odbc">>,
 1024:                                       <<"settings">> => <<"DSN=mydb">>})),
 1025:     ?err(pool_conn_raw(<<"rdbms">>, #{<<"driver">> => <<"mysql">>,
 1026:                                       <<"settings">> => <<"DSN=mydb">>})),
 1027:     ?err(pool_conn_raw(<<"rdbms">>, #{<<"driver">> => <<"odbc">>,
 1028:                                       <<"settings">> => true})),
 1029:     ?err(pool_conn_raw(<<"rdbms">>, #{<<"driver">> => <<"odbc">>})).
 1030: 
 1031: pool_rdbms_keepalive_interval(_Config) ->
 1032:     ?cfg(pool_config({rdbms, global, default, [], [{server, "DSN=mydb"},
 1033:                                                    {keepalive_interval, 1000}]}),
 1034:          pool_conn_raw(<<"rdbms">>, #{<<"driver">> => <<"odbc">>,
 1035:                                       <<"settings">> => <<"DSN=mydb">>,
 1036:                                       <<"keepalive_interval">> => 1000})),
 1037:     ?err(pool_conn_raw(<<"rdbms">>, #{<<"driver">> => <<"odbc">>,
 1038:                                       <<"settings">> => <<"DSN=mydb">>,
 1039:                                       <<"keepalive_interval">> => false})).
 1040: 
 1041: pool_rdbms_server(_Config) ->
 1042:     ServerOpts = rdbms_opts(),
 1043:     ?cfg(pool_config({rdbms, global, default, [],
 1044:                       [{server, {pgsql, "localhost", "db", "dbuser", "secret"}}]}),
 1045:          pool_conn_raw(<<"rdbms">>, ServerOpts)),
 1046:     ?err(pool_conn_raw(<<"rdbms">>, ServerOpts#{<<"driver">> := <<"odbc">>})),
 1047:     [?err(pool_conn_raw(<<"rdbms">>, maps:without([K], ServerOpts))) ||
 1048:         K <- maps:keys(ServerOpts)],
 1049:     [?err(pool_conn_raw(<<"rdbms">>, ServerOpts#{K := 123})) ||
 1050:         K <- maps:keys(ServerOpts)].
 1051: 
 1052: pool_rdbms_port(_Config) ->
 1053:     ServerOpts = rdbms_opts(),
 1054:     ?cfg(pool_config({rdbms, global, default, [],
 1055:                       [{server, {pgsql, "localhost", 1234, "db", "dbuser", "secret"}}]}),
 1056:          pool_conn_raw(<<"rdbms">>, ServerOpts#{<<"port">> => 1234})),
 1057:     ?err(pool_conn_raw(<<"rdbms">>, ServerOpts#{<<"port">> => <<"airport">>})).
 1058: 
 1059: pool_rdbms_tls(_Config) ->
 1060:     ServerOpts = rdbms_opts(),
 1061:     ?cfg(pool_config({rdbms, global, default, [],
 1062:                       [{server, {pgsql, "localhost", "db", "dbuser", "secret",
 1063:                                  [{ssl, required}]}}]}),
 1064:          pool_conn_raw(<<"rdbms">>, ServerOpts#{<<"tls">> => #{<<"required">> => true}})),
 1065:     ?cfg(pool_config({rdbms, global, default, [],
 1066:                       [{server, {pgsql, "localhost", "db", "dbuser", "secret",
 1067:                                  [{ssl, true}]}}]}),
 1068:          pool_conn_raw(<<"rdbms">>, ServerOpts#{<<"tls">> => #{}})),
 1069:     ?cfg(pool_config({rdbms, global, default, [],
 1070:                       [{server, {mysql, "localhost", "db", "dbuser", "secret", []}}]}),
 1071:          pool_conn_raw(<<"rdbms">>, ServerOpts#{<<"driver">> => <<"mysql">>,
 1072:                                                 <<"tls">> => #{}})),
 1073:     ?cfg(pool_config({rdbms, global, default, [],
 1074:                       [{server, {pgsql, "localhost", 1234, "db", "dbuser", "secret",
 1075:                                  [{ssl, true}]}}]}),
 1076:          pool_conn_raw(<<"rdbms">>, ServerOpts#{<<"tls">> => #{},
 1077:                                                 <<"port">> => 1234})),
 1078: 
 1079:     %% one option tested here as they are all checked by 'listen_tls_*' tests
 1080:     ?cfg(pool_config({rdbms, global, default, [],
 1081:                       [{server, {pgsql, "localhost", "db", "dbuser", "secret",
 1082:                                  [{ssl, true}, {ssl_opts, [{certfile, "cert.pem"}]}]}}]}),
 1083:          pool_conn_raw(<<"rdbms">>, ServerOpts#{<<"tls">> =>
 1084:                                                     #{<<"certfile">> => <<"cert.pem">>}})),
 1085:     ?err(pool_conn_raw(<<"rdbms">>, ServerOpts#{<<"tls">> =>
 1086:                                                     #{<<"certfile">> => true}})),
 1087:     ?err(pool_conn_raw(<<"rdbms">>, ServerOpts#{<<"tls">> => <<"secure">>})).
 1088: 
 1089: pool_http_host(_Config) ->
 1090:     ?cfg(pool_config({http, global, default, [], [{server, "https://localhost:8443"}]}),
 1091:          pool_conn_raw(<<"http">>, #{<<"host">> => <<"https://localhost:8443">>})),
 1092:     ?err(pool_conn_raw(<<"http">>, #{<<"host">> => 8443})),
 1093:     ?err(pool_conn_raw(<<"http">>, #{<<"host">> => ""})).
 1094: 
 1095: pool_http_path_prefix(_Config) ->
 1096:     ?cfg(pool_config({http, global, default, [], [{path_prefix, "/"}]}),
 1097:          pool_conn_raw(<<"http">>, #{<<"path_prefix">> => <<"/">>})),
 1098:     ?err(pool_conn_raw(<<"http">>, #{<<"path_prefix">> => 8443})),
 1099:     ?err(pool_conn_raw(<<"http">>, #{<<"path_prefix">> => ""})).
 1100: 
 1101: pool_http_request_timeout(_Config) ->
 1102:     ?cfg(pool_config({http, global, default, [], [{request_timeout, 2000}]}),
 1103:          pool_conn_raw(<<"http">>, #{<<"request_timeout">> => 2000})),
 1104:     ?err(pool_conn_raw(<<"http">>, #{<<"request_timeout">> => -1000})),
 1105:     ?err(pool_conn_raw(<<"http">>, #{<<"request_timeout">> => <<"infinity">>})).
 1106: 
 1107: pool_http_tls(_Config) ->
 1108:     ?cfg(pool_config({http, global, default, [], [{http_opts, [{certfile, "cert.pem"} ]}]}),
 1109:          pool_conn_raw(<<"http">>, #{<<"tls">> => #{<<"certfile">> => <<"cert.pem">>}})),
 1110:     ?err(pool_conn_raw(<<"http">>, #{<<"tls">> => #{<<"certfile">> => true}})),
 1111:     ?err(pool_conn_raw(<<"http">>, #{<<"tls">> => <<"secure">>})).
 1112: 
 1113: pool_redis_host(_Config) ->
 1114:     ?cfg(pool_config({redis, global, default, [], [{host, "localhost"}]}),
 1115:          pool_conn_raw(<<"redis">>, #{<<"host">> => <<"localhost">>})),
 1116:     ?err(pool_conn_raw(<<"redis">>, #{<<"host">> => 8443})),
 1117:     ?err(pool_conn_raw(<<"redis">>, #{<<"host">> => ""})).
 1118: 
 1119: pool_redis_port(_Config) ->
 1120:     ?cfg(pool_config({redis, global, default, [], [{port, 6379}]}),
 1121:          pool_conn_raw(<<"redis">>, #{<<"port">> => 6379})),
 1122:     ?err(pool_conn_raw(<<"redis">>, #{<<"port">> => 666666})),
 1123:     ?err(pool_conn_raw(<<"redis">>, #{<<"port">> => <<"airport">>})).
 1124: 
 1125: pool_redis_database(_Config) ->
 1126:     ?cfg(pool_config({redis, global, default, [], [{database, 0}]}),
 1127:          pool_conn_raw(<<"redis">>, #{<<"database">> => 0})),
 1128:     ?err(pool_conn_raw(<<"redis">>, #{<<"database">> => -1})),
 1129:     ?err(pool_conn_raw(<<"redis">>, #{<<"database">> => <<"my_database">>})).
 1130: 
 1131: pool_redis_password(_Config) ->
 1132:     ?cfg(pool_config({redis, global, default, [], [{password, ""}]}),
 1133:          pool_conn_raw(<<"redis">>, #{<<"password">> => <<"">>})),
 1134:     ?cfg(pool_config({redis, global, default, [], [{password, "password1"}]}),
 1135:          pool_conn_raw(<<"redis">>, #{<<"password">> => <<"password1">>})),
 1136:     ?err(pool_conn_raw(<<"redis">>, #{<<"password">> => 0})).
 1137: 
 1138: pool_riak_address(_Config) ->
 1139:     ?cfg(pool_config({riak, global, default, [], [{address, "127.0.0.1"}]}),
 1140:          pool_conn_raw(<<"riak">>, #{<<"address">> => <<"127.0.0.1">>})),
 1141:     ?err(pool_conn_raw(<<"riak">>, #{<<"address">> => 66})),
 1142:     ?err(pool_conn_raw(<<"riak">>, #{<<"address">> => <<"">>})).
 1143: 
 1144: pool_riak_port(_Config) ->
 1145:     ?cfg(pool_config({riak, global, default, [], [{port, 8087}]}),
 1146:          pool_conn_raw(<<"riak">>, #{<<"port">> => 8087})),
 1147:     ?err(pool_conn_raw(<<"riak">>, #{<<"port">> => 666666})),
 1148:     ?err(pool_conn_raw(<<"riak">>, #{<<"port">> => <<"airport">>})).
 1149: 
 1150: pool_riak_credentials(_Config) ->
 1151:     ?cfg(pool_config({riak, global, default, [], [{credentials, "user", "pass"}]}),
 1152:          pool_conn_raw(<<"riak">>, #{<<"credentials">> =>
 1153:                                          #{<<"user">> => <<"user">>, <<"password">> => <<"pass">>}})),
 1154:     ?err(pool_conn_raw(<<"riak">>, #{<<"credentials">> => #{<<"user">> => <<"user">>}})),
 1155:     ?err(pool_conn_raw(<<"riak">>, #{<<"credentials">> =>
 1156:                                          #{<<"user">> => <<"">>, <<"password">> => 011001}})).
 1157: 
 1158: pool_riak_cacertfile(_Config) ->
 1159:     ?cfg(pool_config({riak, global, default, [], [{cacertfile, "cacert.pem"}]}),
 1160:          pool_conn_raw(<<"riak">>, #{<<"tls">> => #{<<"cacertfile">> => <<"cacert.pem">>}})),
 1161:     ?err(pool_conn_raw(<<"riak">>, #{<<"cacertfile">> => <<"">>})).
 1162: 
 1163: pool_riak_tls(_Config) ->
 1164:     %% make sure these options are not extracted out of 'ssl_opts'
 1165:     %% all the TLS options are checked by 'listen_tls_*' tests
 1166:     ?cfg(pool_config({riak, global, default, [], [{ssl_opts, [{certfile, "path/to/cert.pem"},
 1167:                                                               {dhfile, "cert.pem"},
 1168:                                                               {keyfile, "path/to/key.pem"}]}]}),
 1169:          pool_conn_raw(<<"riak">>, #{<<"tls">> => #{<<"certfile">> => <<"path/to/cert.pem">>,
 1170:                                                     <<"dhfile">> => <<"cert.pem">>,
 1171:                                                     <<"keyfile">> => <<"path/to/key.pem">>}})),
 1172:     ?err(pool_conn_raw(<<"riak">>, #{<<"tls">> => #{<<"dhfile">> => true}})),
 1173:     ?err(pool_conn_raw(<<"riak">>, #{<<"tls">> => <<"secure">>})).
 1174: 
 1175: pool_cassandra_servers(_Config) ->
 1176:     ?cfg(pool_config({cassandra, global, default, [],
 1177:                       [{servers, [{"cassandra_server1.example.com", 9042},
 1178:                                   {"cassandra_server2.example.com", 9042}]}]}),
 1179:          pool_conn_raw(<<"cassandra">>,
 1180:                        #{<<"servers">> => [#{<<"ip_address">> => <<"cassandra_server1.example.com">>,
 1181:                                              <<"port">> => 9042},
 1182:                                            #{<<"ip_address">> => <<"cassandra_server2.example.com">>,
 1183:                                              <<"port">> => 9042}]})),
 1184:     ?err(pool_conn_raw(<<"cassandra">>,
 1185:                        #{<<"servers">> => #{<<"ip_address">> => <<"cassandra_server1.example.com">>,
 1186:                                             <<"port">> => 9042}})).
 1187: 
 1188: pool_cassandra_keyspace(_Config) ->
 1189:     ?cfg(pool_config({cassandra, global, default, [], [{keyspace, "big_mongooseim"}]}),
 1190:          pool_conn_raw(<<"cassandra">>, #{<<"keyspace">> => <<"big_mongooseim">>})),
 1191:     ?err(pool_conn_raw(<<"cassandra">>, #{<<"keyspace">> => <<"">>})).
 1192: 
 1193: pool_cassandra_auth(_Config) ->
 1194:     ?cfg(pool_config({cassandra, global, default, [], [{auth, {cqerl_auth_plain_handler,
 1195:                                                                [{<<"auser">>, <<"secretpass">>}]
 1196:                                                               }}]}),
 1197:          pool_conn_raw(<<"cassandra">>,
 1198:                        #{<<"auth">> => #{<<"plain">> => #{<<"username">> => <<"auser">>,
 1199:                                                           <<"password">> => <<"secretpass">>}}})),
 1200:     ?err(pool_conn_raw(<<"cassandra">>, #{<<"tls">> => #{<<"verify">> => <<"verify_none">>}})).
 1201: 
 1202: pool_cassandra_tls(_Config) ->
 1203:     %% one option tested here as they are all checked by 'listen_tls_*' tests
 1204:     ?cfg(pool_config({cassandra, global, default, [], [{ssl, [{verify, verify_none}]}]}),
 1205:          pool_conn_raw(<<"cassandra">>, #{<<"tls">> => #{<<"verify_peer">> => false}})),
 1206:     ?err(pool_conn_raw(<<"cassandra">>, #{<<"tls">> => #{<<"verify">> => <<"verify_none">>}})).
 1207: 
 1208: pool_elastic_host(_Config) ->
 1209:     ?cfg(pool_config({elastic, global, default, [], [{host, "localhost"}]}),
 1210:          pool_conn_raw(<<"elastic">>, #{<<"host">> => <<"localhost">>})),
 1211:     ?err(pool_conn_raw(<<"elastic">>, #{<<"host">> => <<"">>})).
 1212: 
 1213: pool_elastic_port(_Config) ->
 1214:     ?cfg(pool_config({elastic, global, default, [], [{port, 9200}]}),
 1215:          pool_conn_raw(<<"elastic">>, #{<<"port">> => 9200})),
 1216:     ?err(pool_conn_raw(<<"elastic">>, #{<<"port">> => 122333})),
 1217:     ?err(pool_conn_raw(<<"elastic">>, #{<<"port">> => <<"airport">>})).
 1218: 
 1219: pool_rabbit_amqp_host(_Config) ->
 1220:     ?cfg(pool_config({rabbit, global, default, [], [{amqp_host, "localhost"}]}),
 1221:          pool_conn_raw(<<"rabbit">>, #{<<"amqp_host">> => <<"localhost">>})),
 1222:     ?err(pool_conn_raw(<<"rabbit">>, #{<<"amqp_host">> => <<"">>})).
 1223: 
 1224: pool_rabbit_amqp_port(_Config) ->
 1225:     ?cfg(pool_config({rabbit, global, default, [], [{amqp_port, 5672}]}),
 1226:          pool_conn_raw(<<"rabbit">>, #{<<"amqp_port">> => 5672})),
 1227:     ?err(pool_conn_raw(<<"rabbit">>, #{<<"amqp_port">> => <<"airport">>})).
 1228: 
 1229: pool_rabbit_amqp_username(_Config) ->
 1230:     ?cfg(pool_config({rabbit, global, default, [], [{amqp_username, "guest"}]}),
 1231:          pool_conn_raw(<<"rabbit">>, #{<<"amqp_username">> => <<"guest">>})),
 1232:     ?err(pool_conn_raw(<<"rabbit">>, #{<<"amqp_username">> => <<"">>})).
 1233: 
 1234: pool_rabbit_amqp_password(_Config) ->
 1235:     ?cfg(pool_config({rabbit, global, default, [], [{amqp_password, "guest"}]}),
 1236:          pool_conn_raw(<<"rabbit">>, #{<<"amqp_password">> => <<"guest">>})),
 1237:     ?err(pool_conn_raw(<<"rabbit">>, #{<<"amqp_password">> => <<"">>})).
 1238: 
 1239: pool_rabbit_amqp_confirms_enabled(_Config) ->
 1240:     ?cfg(pool_config({rabbit, global, default, [], [{confirms_enabled, true}]}),
 1241:          pool_conn_raw(<<"rabbit">>, #{<<"confirms_enabled">> => true})),
 1242:     ?err(pool_conn_raw(<<"rabbit">>, #{<<"confirms_enabled">> => <<"yes">>})).
 1243: 
 1244: pool_rabbit_amqp_max_worker_queue_len(_Config) ->
 1245:     ?cfg(pool_config({rabbit, global, default, [], [{max_worker_queue_len, 100}]}),
 1246:          pool_conn_raw(<<"rabbit">>, #{<<"max_worker_queue_len">> => 100})),
 1247:     ?err(pool_conn_raw(<<"rabbit">>, #{<<"max_worker_queue_len">> => 0})).
 1248: 
 1249: pool_ldap_host(_Config) ->
 1250:     ?cfg(pool_config({ldap, global, default, [], [{host, "localhost"}]}),
 1251:          pool_conn_raw(<<"ldap">>, #{<<"host">> => <<"localhost">>})),
 1252:     ?err(pool_conn_raw(<<"ldap">>, #{<<"host">> => <<"">>})).
 1253: 
 1254: pool_ldap_port(_Config) ->
 1255:     ?cfg(pool_config({ldap, global, default, [], [{port, 389}]}),
 1256:          pool_conn_raw(<<"ldap">>, #{<<"port">> => 389})),
 1257:     ?err(pool_conn_raw(<<"ldap">>, #{<<"port">> => <<"airport">>})).
 1258: 
 1259: pool_ldap_servers(_Config) ->
 1260:     ?cfg(pool_config({ldap, global, default, [],
 1261:                       [{servers, ["primary-ldap-server.example.com",
 1262:                                   "secondary-ldap-server.example.com"]}]}),
 1263:          pool_conn_raw(<<"ldap">>, #{<<"servers">> => [<<"primary-ldap-server.example.com">>,
 1264:                                                        <<"secondary-ldap-server.example.com">>]})),
 1265:     ?err(pool_conn_raw(<<"ldap">>, #{<<"servers">> => #{<<"server">> => <<"example.com">>}})).
 1266: 
 1267: pool_ldap_encrypt(_Config) ->
 1268:     ?cfg(pool_config({ldap, global, default, [], [{encrypt, none}]}),
 1269:          pool_conn_raw(<<"ldap">>, #{<<"encrypt">> => <<"none">>})),
 1270:     ?err(pool_conn_raw(<<"ldap">>, #{<<"encrypt">> => true})).
 1271: 
 1272: pool_ldap_rootdn(_Config) ->
 1273:     ?cfg(pool_config({ldap, global, default, [], [{rootdn, ""}]}),
 1274:          pool_conn_raw(<<"ldap">>, #{<<"rootdn">> => <<"">>})),
 1275:     ?err(pool_conn_raw(<<"ldap">>, #{<<"rootdn">> => false})).
 1276: 
 1277: pool_ldap_password(_Config) ->
 1278:     ?cfg(pool_config({ldap, global, default, [], [{password, "pass"}]}),
 1279:          pool_conn_raw(<<"ldap">>, #{<<"password">> => <<"pass">>})),
 1280:     ?err(pool_conn_raw(<<"ldap">>, #{<<"password">> => true})).
 1281: 
 1282: pool_ldap_connect_interval(_Config) ->
 1283:     ?cfg(pool_config({ldap, global, default, [], [{connect_interval, 10000}]}),
 1284:          pool_conn_raw(<<"ldap">>, #{<<"connect_interval">> => 10000})),
 1285:     ?err(pool_conn_raw(<<"ldap">>, #{<<"connect_interval">> => <<"infinity">>})).
 1286: 
 1287: pool_ldap_tls(_Config) ->
 1288:     %% one option tested here as they are all checked by 'listen_tls_*' tests
 1289:     ?cfg(pool_config({ldap, global, default, [], [{tls_options, [{verify, verify_peer}]}]}),
 1290:          pool_conn_raw(<<"ldap">>, #{<<"tls">> => #{<<"verify_peer">> => true}})),
 1291:     ?err(pool_conn_raw(<<"ldap">>, #{<<"tls">> => #{<<"verify">> => <<"verify_none">>}})).
 1292: 
 1293: %% tests: shaper, acl, access
 1294: shaper(_Config) ->
 1295:     ?cfgh({shaper, normal}, {maxrate, 1000},
 1296:           #{<<"shaper">> => #{<<"normal">> => #{<<"max_rate">> => 1000}}}),
 1297:     ?errh(#{<<"shaper">> => #{<<"unlimited">> => #{<<"max_rate">> => <<"infinity">>}}}),
 1298:     ?errh(#{<<"shaper">> => #{<<"fast">> => #{}}}).
 1299: 
 1300: acl(_Config) ->
 1301:     ?cfgh({acl, local}, [all],
 1302:           #{<<"acl">> => #{<<"local">> => [#{<<"match">> => <<"all">>}]}}),
 1303:     ?cfgh({acl, local}, [{user_regexp, <<>>}],
 1304:           #{<<"acl">> => #{<<"local">> => [#{<<"user_regexp">> => <<>>}]}}),
 1305:     ?cfgh({acl, alice}, [{node_regexp, <<"ali.*">>, <<".*host">>}],
 1306:           #{<<"acl">> => #{<<"alice">> => [#{<<"user_regexp">> => <<"ali.*">>,
 1307:                                              <<"server_regexp">> => <<".*host">>}]}}),
 1308:     ?cfgh({acl, alice}, [{user, <<"alice">>, <<"localhost">>}],
 1309:           #{<<"acl">> => #{<<"alice">> => [#{<<"user">> => <<"alice">>,
 1310:                                              <<"server">> => <<"localhost">>}]}}),
 1311:     ?errh(#{<<"acl">> => #{<<"local">> => <<"everybody">>}}),
 1312:     ?errh(#{<<"acl">> => #{<<"alice">> => [#{<<"user_glob">> => <<"a*">>,
 1313:                                              <<"server_blog">> => <<"bloghost">>}]}}).
 1314: 
 1315: access(_Config) ->
 1316:     ?cfgh({access, c2s}, [{deny, blocked}, {allow, all}],
 1317:           #{<<"access">> => #{<<"c2s">> => [#{<<"acl">> => <<"blocked">>,
 1318:                                               <<"value">> => <<"deny">>},
 1319:                                             #{<<"acl">> => <<"all">>,
 1320:                                               <<"value">> => <<"allow">>}]}}),
 1321:     ?cfgh({access, max_user_sessions}, [{10, all}],
 1322:           #{<<"access">> => #{<<"max_user_sessions">> => [#{<<"acl">> => <<"all">>,
 1323:                                                             <<"value">> => 10}]}}),
 1324:     ?errh(#{<<"access">> => #{<<"max_user_sessions">> => [#{<<"acl">> => <<"all">>}]}}),
 1325:     ?errh(#{<<"access">> => #{<<"max_user_sessions">> => [#{<<"value">> => 10}]}}),
 1326:     ?errh(#{<<"access">> => #{<<"max_user_sessions">> => [#{<<"acl">> => 10,
 1327:                                                             <<"value">> => 10}]}}).
 1328: 
 1329: %% tests: s2s
 1330: 
 1331: s2s_dns_timeout(_Config) ->
 1332:     ?cfg(s2s_dns_options, [{timeout, 5}], #{<<"s2s">> => #{<<"dns">> => #{<<"timeout">> => 5}}}),
 1333:     ?err(#{<<"s2s">> => #{<<"dns">> => #{<<"timeout">> => 0}}}).
 1334: 
 1335: s2s_dns_retries(_Config) ->
 1336:     ?cfg(s2s_dns_options, [{retries, 1}], #{<<"s2s">> => #{<<"dns">> => #{<<"retries">> => 1}}}),
 1337:     ?err(#{<<"s2s">> => #{<<"dns">> => #{<<"retries">> => 0}}}).
 1338: 
 1339: s2s_outgoing_port(_Config) ->
 1340:     ?cfg(outgoing_s2s_port, 5270, #{<<"s2s">> => #{<<"outgoing">> => #{<<"port">> => 5270}}}),
 1341:     ?err(#{<<"s2s">> => #{<<"outgoing">> => #{<<"port">> => <<"http">>}}}).
 1342: 
 1343: s2s_outgoing_ip_versions(_Config) ->
 1344:     ?cfg(outgoing_s2s_families, [ipv6, ipv4],
 1345:          #{<<"s2s">> => #{<<"outgoing">> => #{<<"ip_versions">> => [6, 4]}}}),
 1346:     ?err(#{<<"s2s">> => #{<<"outgoing">> => #{<<"ip_versions">> => []}}}),
 1347:     ?err(#{<<"s2s">> => #{<<"outgoing">> => #{<<"ip_versions">> => [<<"http">>]}}}).
 1348: 
 1349: s2s_outgoing_timeout(_Config) ->
 1350:     ?cfg(outgoing_s2s_timeout, 5,
 1351:          #{<<"s2s">> => #{<<"outgoing">> => #{<<"connection_timeout">> => 5}}}),
 1352:     ?cfg(outgoing_s2s_timeout, infinity,
 1353:          #{<<"s2s">> => #{<<"outgoing">> => #{<<"connection_timeout">> => <<"infinity">>}}}),
 1354:     ?err(#{<<"s2s">> => #{<<"outgoing">> => #{<<"connection_timeout">> => 0}}}).
 1355: 
 1356: s2s_use_starttls(_Config) ->
 1357:     ?cfg(s2s_use_starttls, required, #{<<"s2s">> => #{<<"use_starttls">> => <<"required">>}}),
 1358:     ?err(#{<<"s2s">> => #{<<"use_starttls">> => <<"unnecessary">>}}).
 1359: 
 1360: s2s_certfile(_Config) ->
 1361:     ?cfg(s2s_certfile, "cert.pem",  #{<<"s2s">> => #{<<"certfile">> => <<"cert.pem">>}}),
 1362:     ?err(#{<<"s2s">> => #{<<"certfile">> => []}}).
 1363: 
 1364: s2s_default_policy(_Config) ->
 1365:     ?cfgh(s2s_default_policy, deny, #{<<"s2s">> => #{<<"default_policy">> => <<"deny">>}}),
 1366:     ?errh(#{<<"s2s">> => #{<<"default_policy">> => <<"ask">>}}).
 1367: 
 1368: s2s_host_policy(_Config) ->
 1369:     Policy = #{<<"host">> => <<"host1">>,
 1370:                <<"policy">> => <<"allow">>},
 1371:     ?cfgh(s2s_host_policy, #{<<"host1">> => allow},
 1372:           #{<<"s2s">> => #{<<"host_policy">> => [Policy]}}),
 1373:     ?cfgh(s2s_host_policy, #{<<"host1">> => allow,
 1374:                              <<"host2">> => deny},
 1375:           #{<<"s2s">> => #{<<"host_policy">> => [Policy, #{<<"host">> => <<"host2">>,
 1376:                                                            <<"policy">> => <<"deny">>}]}}),
 1377:     ?errh(#{<<"s2s">> => #{<<"host_policy">> => [maps:without([<<"host">>], Policy)]}}),
 1378:     ?errh(#{<<"s2s">> => #{<<"host_policy">> => [maps:without([<<"policy">>], Policy)]}}),
 1379:     ?errh(#{<<"s2s">> => #{<<"host_policy">> => [Policy#{<<"host">> => <<>>}]}}),
 1380:     ?errh(#{<<"s2s">> => #{<<"host_policy">> => [Policy#{<<"policy">> => <<"huh">>}]}}),
 1381:     ?errh(#{<<"s2s">> => #{<<"host_policy">> => [Policy,
 1382:                                                  Policy#{<<"policy">> => <<"deny">>}]}}).
 1383: 
 1384: s2s_address(_Config) ->
 1385:     Addr = #{<<"host">> => <<"host1">>,
 1386:              <<"ip_address">> => <<"192.168.1.2">>,
 1387:              <<"port">> => 5321},
 1388:     ?cfg(s2s_address, #{<<"host1">> => {"192.168.1.2", 5321}},
 1389:          #{<<"s2s">> => #{<<"address">> => [Addr]}}),
 1390:     ?cfg(s2s_address, #{<<"host1">> => "192.168.1.2"},
 1391:          #{<<"s2s">> => #{<<"address">> => [maps:without([<<"port">>], Addr)]}}),
 1392:     ?err(#{<<"s2s">> => #{<<"address">> => [maps:without([<<"host">>], Addr)]}}),
 1393:     ?err(#{<<"s2s">> => #{<<"address">> => [maps:without([<<"ip_address">>], Addr)]}}),
 1394:     ?err(#{<<"s2s">> => #{<<"address">> => [Addr#{<<"host">> => <<>>}]}}),
 1395:     ?err(#{<<"s2s">> => #{<<"address">> => [Addr#{<<"ip_address">> => <<"host2">>}]}}),
 1396:     ?err(#{<<"s2s">> => #{<<"address">> => [Addr#{<<"port">> => <<"seaport">>}]}}),
 1397:     ?err(#{<<"s2s">> => #{<<"address">> => [Addr, maps:remove(<<"port">>, Addr)]}}).
 1398: 
 1399: s2s_ciphers(_Config) ->
 1400:     ?cfg(s2s_ciphers, "TLSv1.2:TLSv1.3",
 1401:          #{<<"s2s">> => #{<<"ciphers">> => <<"TLSv1.2:TLSv1.3">>}}),
 1402:     ?err(#{<<"s2s">> => #{<<"ciphers">> => [<<"cipher1">>, <<"cipher2">>]}}).
 1403: 
 1404: s2s_domain_certfile(_Config) ->
 1405:     DomCert = #{<<"domain">> => <<"myxmpp.com">>,
 1406:                 <<"certfile">> => <<"mycert.pem">>},
 1407:     ?cfg(domain_certfile, #{<<"myxmpp.com">> => "mycert.pem"},
 1408:          #{<<"s2s">> => #{<<"domain_certfile">> => [DomCert]}}),
 1409:     [?err(#{<<"s2s">> => #{<<"domain_certfile">> => [maps:without([K], DomCert)]}})
 1410:      || K <- maps:keys(DomCert)],
 1411:     [?err(#{<<"s2s">> => #{<<"domain_certfile">> => [DomCert#{K := <<>>}]}})
 1412:      || K <- maps:keys(DomCert)],
 1413:     ?err(#{<<"s2s">> => #{<<"domain_certfile">> => [DomCert, DomCert]}}).
 1414: 
 1415: s2s_shared(_Config) ->
 1416:     ?cfgh(s2s_shared, <<"secret">>, #{<<"s2s">> => #{<<"shared">> => <<"secret">>}}),
 1417:     ?errh(#{<<"s2s">> => #{<<"shared">> => 536837}}).
 1418: 
 1419: s2s_max_retry_delay(_Config) ->
 1420:     ?cfgh(s2s_max_retry_delay, 120, #{<<"s2s">> => #{<<"max_retry_delay">> => 120}}),
 1421:     ?errh(#{<<"s2s">> => #{<<"max_retry_delay">> => 0}}).
 1422: 
 1423: %% modules
 1424: 
 1425: mod_adhoc(_Config) ->
 1426:     check_iqdisc(mod_adhoc),
 1427:     M = fun(K, V) -> modopts(mod_adhoc, [{K, V}]) end,
 1428:     T = fun(K, V) -> #{<<"modules">> => #{<<"mod_adhoc">> => #{K => V}}} end,
 1429:     %% report_commands_node is boolean
 1430:     ?cfgh(M(report_commands_node, true), T(<<"report_commands_node">>, true)),
 1431:     ?cfgh(M(report_commands_node, false), T(<<"report_commands_node">>, false)),
 1432:     %% not boolean
 1433:     ?errh(T(<<"report_commands_node">>, <<"hello">>)).
 1434: 
 1435: mod_auth_token(_Config) ->
 1436:     check_iqdisc(mod_auth_token),
 1437:     P = fun(X) ->
 1438:                 Opts = #{<<"validity_period">> => X},
 1439:                 #{<<"modules">> => #{<<"mod_auth_token">> => Opts}}
 1440:         end,
 1441:     ?cfgh(modopts(mod_auth_token, [{{validity_period, access}, {13, minutes}},
 1442:                                    {{validity_period, refresh}, {31, days}}]),
 1443:           P([#{<<"token">> => <<"access">>, <<"value">> => 13, <<"unit">> => <<"minutes">>},
 1444:              #{<<"token">> => <<"refresh">>, <<"value">> => 31, <<"unit">> => <<"days">>}])),
 1445:     ?errh(P([#{<<"token">> => <<"access">>, <<"value">> => <<"13">>,
 1446:                <<"unit">> => <<"minutes">>}])),
 1447:     ?errh(P([#{<<"token">> => <<"access">>, <<"value">> => 13, <<"unit">> => <<"minute">>}])),
 1448:     ?errh(P([#{<<"token">> => <<"Access">>, <<"value">> => 13, <<"unit">> => <<"minutes">>}])),
 1449:     ?errh(P([#{<<"value">> => 13, <<"unit">> => <<"minutes">>}])),
 1450:     ?errh(P([#{<<"token">> => <<"access">>, <<"unit">> => <<"minutes">>}])),
 1451:     ?errh(P([#{<<"token">> => <<"access">>, <<"value">> => 13}])).
 1452: 
 1453: mod_bosh(_Config) ->
 1454:     T = fun(K, V) -> #{<<"modules">> => #{<<"mod_bosh">> => #{K => V}}} end,
 1455:     M = fun(K, V) -> modopts(mod_bosh, [{K, V}]) end,
 1456:     ?cfgh(M(inactivity, 10), T(<<"inactivity">>, 10)),
 1457:     ?cfgh(M(inactivity, infinity), T(<<"inactivity">>, <<"infinity">>)),
 1458:     ?cfgh(M(inactivity, 10), T(<<"inactivity">>, 10)),
 1459:     ?cfgh(M(max_wait, infinity), T(<<"max_wait">>, <<"infinity">>)),
 1460:     ?cfgh(M(server_acks, true), T(<<"server_acks">>, true)),
 1461:     ?cfgh(M(server_acks, false), T(<<"server_acks">>, false)),
 1462:     ?cfgh(M(maxpause, 10), T(<<"max_pause">>, 10)),
 1463:     ?errh(T(<<"inactivity">>, -1)),
 1464:     ?errh(T(<<"inactivity">>, <<"10">>)),
 1465:     ?errh(T(<<"inactivity">>, <<"inactivity">>)),
 1466:     ?errh(T(<<"max_wait">>, <<"10">>)),
 1467:     ?errh(T(<<"max_wait">>, -1)),
 1468:     ?errh(T(<<"server_acks">>, -1)),
 1469:     ?errh(T(<<"maxpause">>, 0)).
 1470: 
 1471: mod_caps(_Config) ->
 1472:     T = fun(K, V) -> #{<<"modules">> => #{<<"mod_caps">> => #{K => V}}} end,
 1473:     M = fun(K, V) -> modopts(mod_caps, [{K, V}]) end,
 1474:     ?cfgh(M(cache_size, 10), T(<<"cache_size">>, 10)),
 1475:     ?cfgh(M(cache_life_time, 10), T(<<"cache_life_time">>, 10)),
 1476:     ?errh(T(<<"cache_size">>, 0)),
 1477:     ?errh(T(<<"cache_size">>, <<"infinity">>)),
 1478:     ?errh(T(<<"cache_life_time">>, 0)),
 1479:     ?errh(T(<<"cache_life_time">>, <<"infinity">>)).
 1480: 
 1481: mod_cache_users(_Config) ->
 1482:     T = fun(K, V) -> #{<<"modules">> => #{<<"mod_cache_users">> => #{K => V}}} end,
 1483:     M = fun(K, V) -> modopts(mod_cache_users, [{K, V}]) end,
 1484:     ?cfgh(M(time_to_live, 8600), T(<<"time_to_live">>, 8600)),
 1485:     ?cfgh(M(time_to_live, infinity), T(<<"time_to_live">>, <<"infinity">>)),
 1486:     ?cfgh(M(number_of_segments, 10), T(<<"number_of_segments">>, 10)),
 1487:     ?cfgh(M(strategy, fifo), T(<<"strategy">>, <<"fifo">>)),
 1488:     ?errh(T(<<"time_to_live">>, 0)),
 1489:     ?errh(T(<<"strategy">>, <<"lifo">>)),
 1490:     ?errh(T(<<"number_of_segments">>, 0)),
 1491:     ?errh(T(<<"number_of_segments">>, <<"infinity">>)).
 1492: 
 1493: mod_carboncopy(_Config) ->
 1494:     check_iqdisc(mod_carboncopy).
 1495: 
 1496: mod_csi(_Config) ->
 1497:     T = fun(K, V) -> #{<<"modules">> => #{<<"mod_csi">> => #{K => V}}} end,
 1498:     M = fun(K, V) -> modopts(mod_csi, [{K, V}]) end,
 1499:     ?cfgh(M(buffer_max, 10), T(<<"buffer_max">>, 10)),
 1500:     ?cfgh(M(buffer_max, infinity), T(<<"buffer_max">>, <<"infinity">>)),
 1501:     ?errh(T(<<"buffer_max">>, -1)).
 1502: 
 1503: mod_disco(_Config) ->
 1504:     check_iqdisc(mod_disco),
 1505:     T = fun(K, V) -> #{<<"modules">> => #{<<"mod_disco">> => #{K => V}}} end,
 1506:     ?cfgh(modopts(mod_disco, [{users_can_see_hidden_services, true}]),
 1507:           T(<<"users_can_see_hidden_services">>, true)),
 1508:     ?cfgh(modopts(mod_disco, [{users_can_see_hidden_services, false}]),
 1509:           T(<<"users_can_see_hidden_services">>, false)),
 1510:     %% extra_domains are binaries
 1511:     ?cfgh(modopts(mod_disco, [{extra_domains, [<<"localhost">>, <<"erlang-solutions.com">>]}]),
 1512:           T(<<"extra_domains">>, [<<"localhost">>, <<"erlang-solutions.com">>])),
 1513:     ?cfgh(modopts(mod_disco, [{extra_domains, []}]),
 1514:           T(<<"extra_domains">>, [])),
 1515:     Info = #{<<"name">> => <<"abuse-address">>,
 1516:              <<"urls">> => [<<"admin@example.com">>]},
 1517:     SpiritUrls = [<<"spirit1@localhost">>, <<"spirit2@localhost">>],
 1518:     ?cfgh(modopts(mod_disco, [{server_info, [[{name, <<"abuse-address">>},
 1519:                                               {urls, [<<"admin@example.com">>]}],
 1520:                                              [{modules, [mod_muc, mod_disco]},
 1521:                                               {name, <<"friendly-spirits">>},
 1522:                                               {urls, SpiritUrls}]
 1523:                                             ]}
 1524:                              ]),
 1525:           T(<<"server_info">>, [Info, #{<<"modules">> => [<<"mod_muc">>, <<"mod_disco">>],
 1526:                                         <<"name">> => <<"friendly-spirits">>,
 1527:                                         <<"urls">> => SpiritUrls}
 1528:                                ])),
 1529:     ?errh(T(<<"users_can_see_hidden_services">>, 1)),
 1530:     ?errh(T(<<"users_can_see_hidden_services">>, <<"true">>)),
 1531:     ?errh(T(<<"extra_domains">>, [<<"user@localhost">>])),
 1532:     ?errh(T(<<"extra_domains">>, [1])),
 1533:     ?errh(T(<<"extra_domains">>, <<"domains domains domains">>)),
 1534:     ?errh(T(<<"server_info">>, [Info#{<<"name">> => 1}])),
 1535:     ?errh(T(<<"server_info">>, [Info#{<<"name">> => <<"">>}])),
 1536:     ?errh(T(<<"server_info">>, [Info#{<<"modules">> => <<"roll">>}])),
 1537:     ?errh(T(<<"server_info">>, [Info#{<<"modules">> => [<<"meow_meow_meow">>]}])),
 1538:     ?errh(T(<<"server_info">>, [Info#{<<"urls">> => [1]}])),
 1539:     ?errh(T(<<"server_info">>, [Info#{<<"urls">> => [<<"">>]}])),
 1540:     ?errh(T(<<"server_info">>, [maps:remove(<<"name">>, Info)])),
 1541:     ?errh(T(<<"server_info">>, [maps:remove(<<"urls">>, Info)])).
 1542: 
 1543: mod_extdisco(_Config) ->
 1544:     T = fun(Opts) -> #{<<"modules">> =>
 1545:                            #{<<"mod_extdisco">> =>
 1546:                                  #{<<"service">> => [Opts]}}}
 1547:         end,
 1548:     M = fun(Opts) -> modopts(mod_extdisco, [Opts]) end,
 1549:     RequiredOpts = #{
 1550:                      <<"type">> => <<"stun">>,
 1551:                      <<"host">> => <<"stun1">>},
 1552:     ExpectedCfg = [{host, "stun1"},
 1553:                    {type, stun}],
 1554:     ?cfgh(M(ExpectedCfg), T(RequiredOpts)),
 1555:     ?cfgh(M(ExpectedCfg ++ [{port, 3478}]),
 1556:           T(RequiredOpts#{<<"port">> => 3478})),
 1557:     ?cfgh(M(ExpectedCfg ++ [{transport, "udp"}]),
 1558:           T(RequiredOpts#{<<"transport">> => <<"udp">>})),
 1559:     ?cfgh(M(ExpectedCfg ++ [{username, "username"}]),
 1560:           T(RequiredOpts#{<<"username">> => <<"username">>})),
 1561:     ?cfgh(M(ExpectedCfg ++ [{password, "password"}]),
 1562:           T(RequiredOpts#{<<"password">> => <<"password">>})),
 1563:     [?errh(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)],
 1564:     [?errh(T(RequiredOpts#{Key => 1})) || Key <- maps:keys(RequiredOpts)],
 1565:     ?errh(T(RequiredOpts#{<<"type">> => <<"">>})),
 1566:     ?errh(T(RequiredOpts#{<<"host">> => <<"">>})),
 1567:     ?errh(T(RequiredOpts#{<<"port">> => -1})),
 1568:     ?errh(T(RequiredOpts#{<<"transport">> => <<"">>})),
 1569:     ?errh(T(RequiredOpts#{<<"username">> => <<"">>})),
 1570:     ?errh(T(RequiredOpts#{<<"password">> => <<"">>})).
 1571: 
 1572: mod_inbox(_Config) ->
 1573:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_inbox">> => Opts}} end,
 1574:     M = fun(Opts) -> modopts(mod_inbox, Opts) end,
 1575:     ChatMarkers = [<<"displayed">>, <<"received">>, <<"acknowledged">>],
 1576:     ?cfgh(M([{reset_markers, ChatMarkers}]),
 1577:           T(#{<<"reset_markers">> => ChatMarkers})),
 1578:     ?cfgh(M([{groupchat, [muc, muclight]}]),
 1579:           T(#{<<"groupchat">> => [<<"muc">>, <<"muclight">>]})),
 1580:     ?cfgh(M([{aff_changes, true}]),
 1581:           T(#{<<"aff_changes">> => true})),
 1582:     ?cfgh(M([{remove_on_kicked, false}]),
 1583:           T(#{<<"remove_on_kicked">> => false})),
 1584:     ?errh(T(#{<<"reset_markers">> => 1})),
 1585:     ?errh(T(#{<<"reset_markers">> => [<<"destroyed">>]})),
 1586:     ?errh(T(#{<<"groupchat">> => [<<"test">>]})),
 1587:     ?errh(T(#{<<"aff_changes">> => 1})),
 1588:     ?errh(T(#{<<"remove_on_kicked">> => 1})),
 1589:     check_iqdisc(mod_inbox).
 1590: 
 1591: mod_global_distrib(_Config) ->
 1592:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_global_distrib">> => Opts}} end,
 1593:     M = fun(Cfg) -> modopts(mod_global_distrib, Cfg) end,
 1594:     RequiredOpts = global_distrib_required_opts(),
 1595:     ExpectedCfg = global_distrib_expected_config(),
 1596:     ?cfgh(M(ExpectedCfg), T(RequiredOpts)),
 1597:     ?cfgh(M(ExpectedCfg ++ [{message_ttl, 42}]),
 1598:           T(RequiredOpts#{<<"message_ttl">> => 42})),
 1599:     ?cfgh(M(ExpectedCfg ++ [{hosts_refresh_interval, 100}]),
 1600:           T(RequiredOpts#{<<"hosts_refresh_interval">> => 100})),
 1601:     [?errh(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)],
 1602:     ?errh(T(RequiredOpts#{<<"global_host">> => <<"">>})),
 1603:     ?errh(T(RequiredOpts#{<<"local_host">> => <<"">>})),
 1604:     ?errh(T(RequiredOpts#{<<"message_ttl">> => -1})),
 1605:     ?errh(T(RequiredOpts#{<<"hosts_refresh_interval">> => -1})).
 1606: 
 1607: mod_global_distrib_connections(_Config) ->
 1608:     RequiredOpts = global_distrib_required_opts(),
 1609:     T = fun(Opts) -> #{<<"modules">> =>
 1610:                            #{<<"mod_global_distrib">> =>
 1611:                                  RequiredOpts#{<<"connections">> => Opts}}}
 1612:         end,
 1613:     M = fun(Cfg) -> modopts(mod_global_distrib,
 1614:                             global_distrib_expected_config() ++ [{connections, Cfg}])
 1615:         end,
 1616:     ?cfgh(M([]), T(#{})),
 1617:     ?cfgh(M([{connections_per_endpoint, 22}]),
 1618:           T(#{<<"connections_per_endpoint">> => 22})),
 1619:     ?cfgh(M([{endpoint_refresh_interval, 120}]),
 1620:           T(#{<<"endpoint_refresh_interval">> => 120})),
 1621:     ?cfgh(M([{endpoint_refresh_interval_when_empty, 5}]),
 1622:           T(#{<<"endpoint_refresh_interval_when_empty">> => 5})),
 1623:     ?cfgh(M([{disabled_gc_interval, 60}]),
 1624:           T(#{<<"disabled_gc_interval">> => 60})),
 1625:     ?errh(T(#{<<"connections_per_endpoint">> => -1})),
 1626:     ?errh(T(#{<<"endpoint_refresh_interval">> => 0})),
 1627:     ?errh(T(#{<<"endpoint_refresh_interval_when_empty">> => 0})),
 1628:     ?errh(T(#{<<"disabled_gc_interval">> => 0})).
 1629: 
 1630: mod_global_distrib_connections_endpoints(_Config) ->
 1631:     check_mod_global_distrib_endpoints(<<"endpoints">>).
 1632: 
 1633: mod_global_distrib_connections_advertised_endpoints(_Config) ->
 1634:     check_mod_global_distrib_endpoints(<<"advertised_endpoints">>).
 1635: 
 1636: check_mod_global_distrib_endpoints(OptKey) ->
 1637:     CfgKey = binary_to_atom(OptKey, utf8),
 1638:     RequiredModOpts = global_distrib_required_opts(),
 1639:     T = fun(Opts) -> #{<<"modules">> =>
 1640:                            #{<<"mod_global_distrib">> =>
 1641:                                  RequiredModOpts#{<<"connections">> => #{OptKey => Opts}}}}
 1642:         end,
 1643:     M = fun(Cfg) -> modopts(mod_global_distrib,
 1644:                             global_distrib_expected_config() ++
 1645:                                 [{connections, [{CfgKey, Cfg}]}])
 1646:         end,
 1647:     RequiredOpts = #{<<"host">> => <<"172.16.0.2">>,
 1648:                      <<"port">> => 5555},
 1649:     ?cfgh(M([{"172.16.0.2", 5555}]), T([RequiredOpts])),
 1650:     [?errh(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)],
 1651:     ?errh(T([RequiredOpts#{<<"host">> => <<>>}])),
 1652:     ?errh(T([RequiredOpts#{<<"port">> => -1}])).
 1653: 
 1654: mod_global_distrib_connections_tls(_Config) ->
 1655:     RequiredModOpts = global_distrib_required_opts(),
 1656:     T = fun(Opts) -> #{<<"modules">> =>
 1657:                            #{<<"mod_global_distrib">> =>
 1658:                                  RequiredModOpts#{<<"connections">> => #{<<"tls">> => Opts}}}}
 1659:         end,
 1660:     M = fun(Cfg) -> modopts(mod_global_distrib,
 1661:                             global_distrib_expected_config() ++
 1662:                                 [{connections, [{tls_opts, Cfg}]}])
 1663:         end,
 1664:     RequiredOpts = #{<<"certfile">> => <<"priv/cert.pem">>,
 1665:                      <<"cacertfile">> => <<"priv/ca.pem">>},
 1666:     ExpectedCfg = [{certfile, "priv/cert.pem"},
 1667:                    {cafile, "priv/ca.pem"}],
 1668:     ?cfgh(M(ExpectedCfg), T(RequiredOpts)),
 1669:     ?cfgh(M(ExpectedCfg ++ [{ciphers, "TLS_AES_256_GCM_SHA384"}]),
 1670:           T(RequiredOpts#{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>})),
 1671:     ?cfgh(M(ExpectedCfg ++ [{dhfile, "priv/cert.pem"}]),
 1672:           T(RequiredOpts#{<<"dhfile">> => <<"priv/cert.pem">>})),
 1673:     [?errh(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)],
 1674:     ?errh(T(RequiredOpts#{<<"certfile">> => <<"/this/does/not/exist">>})),
 1675:     ?errh(T(RequiredOpts#{<<"cacertfile">> => <<"/this/does/not/exist">>})),
 1676:     ?errh(T(RequiredOpts#{<<"dhfile">> => <<"/this/does/not/exist">>})),
 1677:     ?errh(T(RequiredOpts#{<<"ciphers">> => 42})).
 1678: 
 1679: mod_global_distrib_redis(_Config) ->
 1680:     RequiredModOpts = global_distrib_required_opts(),
 1681:     T = fun(Opts) -> #{<<"modules">> =>
 1682:                            #{<<"mod_global_distrib">> =>
 1683:                                  RequiredModOpts#{<<"redis">> => Opts}}}
 1684:         end,
 1685:     M = fun(Cfg) -> modopts(mod_global_distrib,
 1686:                             global_distrib_expected_config() ++ [{redis, Cfg}])
 1687:         end,
 1688:     ?cfgh(M([]), T(#{})),
 1689:     ?cfgh(M([{pool, global_distrib}]),
 1690:           T(#{<<"pool">> => <<"global_distrib">>})),
 1691:     ?cfgh(M([{expire_after, 120}]),
 1692:           T(#{<<"expire_after">> => 120})),
 1693:     ?cfgh(M([{refresh_after, 60}]),
 1694:           T(#{<<"refresh_after">> => 60})),
 1695:     ?errh(T(#{<<"pool">> => <<"">>})),
 1696:     ?errh(T(#{<<"expire_after">> => 0})),
 1697:     ?errh(T(#{<<"refresh_after">> => -1})).
 1698: 
 1699: mod_global_distrib_cache(_Config) ->
 1700:     RequiredModOpts = global_distrib_required_opts(),
 1701:     T = fun(Opts) -> #{<<"modules">> =>
 1702:                            #{<<"mod_global_distrib">> =>
 1703:                                  RequiredModOpts#{<<"cache">> => Opts}}}
 1704:         end,
 1705:     M = fun(Cfg) -> modopts(mod_global_distrib,
 1706:                             global_distrib_expected_config() ++ [{cache, Cfg}])
 1707:         end,
 1708:     ?cfgh(M([]), T(#{})),
 1709:     ?cfgh(M([{cache_missed, false}]),
 1710:           T(#{<<"cache_missed">> => false})),
 1711:     ?cfgh(M([{domain_lifetime_seconds, 60}]),
 1712:           T(#{<<"domain_lifetime_seconds">> => 60})),
 1713:     ?cfgh(M([{jid_lifetime_seconds, 30}]),
 1714:           T(#{<<"jid_lifetime_seconds">> => 30})),
 1715:     ?cfgh(M([{max_jids, 9999}]),
 1716:           T(#{<<"max_jids">> => 9999})),
 1717:     ?errh(T(#{<<"cache_missed">> => <<"yes">>})),
 1718:     ?errh(T(#{<<"domain_lifetime_seconds">> => -1})),
 1719:     ?errh(T(#{<<"jid_lifetime_seconds">> => -1})),
 1720:     ?errh(T(#{<<"max_jids">> => -1})).
 1721: 
 1722: mod_global_distrib_bounce(_Config) ->
 1723:     RequiredModOpts = global_distrib_required_opts(),
 1724:     T = fun(Opts) -> #{<<"modules">> =>
 1725:                            #{<<"mod_global_distrib">> =>
 1726:                                  RequiredModOpts#{<<"bounce">> => Opts}}}
 1727:         end,
 1728:     M = fun(Cfg) -> modopts(mod_global_distrib,
 1729:                             global_distrib_expected_config() ++ [{bounce, Cfg}])
 1730:         end,
 1731:     ?cfgh(M(false),
 1732:           T(#{<<"enabled">> => false})),
 1733:     ?cfgh(M([]),
 1734:           T(#{<<"enabled">> => true})),
 1735:     ?cfgh(M([{resend_after_ms, 300}]),
 1736:           T(#{<<"resend_after_ms">> => 300})),
 1737:     ?cfgh(M([{max_retries, 3}]),
 1738:           T(#{<<"max_retries">> => 3})),
 1739:     ?errh(T(#{<<"enabled">> => <<"">>})),
 1740:     ?errh(T(#{<<"resend_after_ms">> => -1})),
 1741:     ?errh(T(#{<<"max_retries">> => -1})).
 1742: 
 1743: global_distrib_required_opts() ->
 1744:     #{<<"global_host">> => <<"example.com">>,
 1745:       <<"local_host">> => <<"datacenter1.example.com">>}.
 1746: 
 1747: global_distrib_expected_config() ->
 1748:     [{global_host, "example.com"},
 1749:      {local_host, "datacenter1.example.com"}].
 1750: 
 1751: mod_event_pusher_sns(_Config) ->
 1752:     RequiredOpts = #{<<"access_key_id">> => <<"AKIAIOSFODNN7EXAMPLE">>,
 1753:                      <<"secret_access_key">> => <<"KEY">>,
 1754:                      <<"region">> => <<"eu-west-1">>,
 1755:                      <<"account_id">> => <<"123456789012">>,
 1756:                      <<"sns_host">> => <<"sns.eu-west-1.amazonaws.com">>},
 1757:     ExpectedCfg = [{access_key_id, "AKIAIOSFODNN7EXAMPLE"},
 1758:                    {secret_access_key, "KEY"},
 1759:                    {region, "eu-west-1"},
 1760:                    {account_id, "123456789012"},
 1761:                    {sns_host, "sns.eu-west-1.amazonaws.com"}],
 1762:     T = fun(Opts) -> #{<<"modules">> =>
 1763:                            #{<<"mod_event_pusher">> =>
 1764:                                  #{<<"backend">> => #{<<"sns">> => Opts}}}}
 1765:         end,
 1766:     M = fun(Cfg) -> modopts(mod_event_pusher, [{backends, [{sns, Cfg}]}]) end,
 1767:     ?cfgh(M(ExpectedCfg),
 1768:           T(RequiredOpts)),
 1769:     ?cfgh(M(ExpectedCfg ++ [{presence_updates_topic, "pres"}]),
 1770:           T(RequiredOpts#{<<"presence_updates_topic">> => <<"pres">>})),
 1771:     ?cfgh(M(ExpectedCfg ++ [{pm_messages_topic, "pm"}]),
 1772:           T(RequiredOpts#{<<"pm_messages_topic">> => <<"pm">>})),
 1773:     ?cfgh(M(ExpectedCfg ++ [{muc_messages_topic, "muc"}]),
 1774:           T(RequiredOpts#{<<"muc_messages_topic">> => <<"muc">>})),
 1775:     ?cfgh(M(ExpectedCfg ++ [{plugin_module, mod_event_pusher_sns_defaults}]),
 1776:           T(RequiredOpts#{<<"plugin_module">> => <<"mod_event_pusher_sns_defaults">>})),
 1777:     ?cfgh(M(ExpectedCfg ++ [{pool_size, 10}]),
 1778:           T(RequiredOpts#{<<"pool_size">> => 10})),
 1779:     ?cfgh(M(ExpectedCfg ++ [{publish_retry_count, 1}]),
 1780:           T(RequiredOpts#{<<"publish_retry_count">> => 1})),
 1781:     ?cfgh(M(ExpectedCfg ++ [{publish_retry_time_ms, 100}]),
 1782:           T(RequiredOpts#{<<"publish_retry_time_ms">> => 100})),
 1783:     [?errh(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)],
 1784:     [?errh(T(RequiredOpts#{Key => 1})) || Key <- maps:keys(RequiredOpts)],
 1785:     ?errh(T(RequiredOpts#{<<"presence_updates_topic">> => #{}})),
 1786:     ?errh(T(RequiredOpts#{<<"pm_messages_topic">> => true})),
 1787:     ?errh(T(RequiredOpts#{<<"muc_messages_topic">> => [1, 2]})),
 1788:     ?errh(T(RequiredOpts#{<<"plugin_module">> => <<"plug_and_play">>})),
 1789:     ?errh(T(RequiredOpts#{<<"pool_size">> => 0})),
 1790:     ?errh(T(RequiredOpts#{<<"publish_retry_count">> => -1})),
 1791:     ?errh(T(RequiredOpts#{<<"publish_retry_time_ms">> => -1})).
 1792: 
 1793: mod_event_pusher_push(_Config) ->
 1794:     T = fun(Opts) -> #{<<"modules">> =>
 1795:                            #{<<"mod_event_pusher">> =>
 1796:                                  #{<<"backend">> => #{<<"push">> => Opts}}}}
 1797:         end,
 1798:     M = fun(Cfg) -> modopts(mod_event_pusher, [{backends, [{push, Cfg}]}]) end,
 1799:     ?cfgh(M([{backend, rdbms}]),
 1800:           T(#{<<"backend">> => <<"rdbms">>})),
 1801:     ?cfgh(M([{wpool, [{workers, 200}]}]),
 1802:           T(#{<<"wpool">> => #{<<"workers">> => 200}})),
 1803:     ?cfgh(M([{plugin_module, mod_event_pusher_push_plugin_defaults}]),
 1804:           T(#{<<"plugin_module">> => <<"mod_event_pusher_push_plugin_defaults">>})),
 1805:     ?cfgh(M([{virtual_pubsub_hosts, [{fqdn, <<"host1">>}, {fqdn, <<"host2">>}]}]),
 1806:           T(#{<<"virtual_pubsub_hosts">> => [<<"host1">>, <<"host2">>]})),
 1807:     ?cfgh(M([{virtual_pubsub_hosts, [{prefix, <<"pubsub.">>}, {prefix, <<"pub-sub.">>}]}]),
 1808:           T(#{<<"virtual_pubsub_hosts">> => [<<"pubsub.@HOST@">>, <<"pub-sub.@HOST@">>]})),
 1809:     ?errh(T(#{<<"backend">> => <<"redis">>})),
 1810:     ?errh(T(#{<<"wpool">> => true})),
 1811:     ?errh(T(#{<<"wpool">> => #{<<"workers">> => <<"500">>}})),
 1812:     ?errh(T(#{<<"plugin_module">> => <<"wow_cool_but_missing">>})),
 1813:     ?errh(T(#{<<"plugin_module">> => 1})),
 1814:     ?errh(T(#{<<"virtual_pubsub_hosts">> => [<<"host with whitespace">>]})),
 1815:     ?errh(T(#{<<"virtual_pubsub_hosts">> => [<<"invalid.sub@HOST@">>]})),
 1816:     ?errh(T(#{<<"virtual_pubsub_hosts">> => [<<"invalid.sub.@HOST@.as.well">>]})).
 1817: 
 1818: mod_event_pusher_http(_Config) ->
 1819:     T = fun(Opts) -> #{<<"modules">> =>
 1820:                            #{<<"mod_event_pusher">> =>
 1821:                                  #{<<"backend">> => #{<<"http">> => Opts}}}}
 1822:         end,
 1823:     M = fun(Cfg) -> modopts(mod_event_pusher, [{backends, [{http, Cfg}]}]) end,
 1824:     ?cfgh(M([{pool_name, http_pool}]),
 1825:           T(#{<<"pool_name">> => <<"http_pool">>})),
 1826:     ?cfgh(M([{path, "/notifications"}]),
 1827:           T(#{<<"path">> => <<"/notifications">>})),
 1828:     ?cfgh(M([{callback_module, mod_event_pusher_http_defaults}]),
 1829:           T(#{<<"callback_module">> => <<"mod_event_pusher_http_defaults">>})),
 1830:     ?errh(T(#{<<"pool_name">> => <<>>})),
 1831:     ?errh(T(#{<<"path">> => true})),
 1832:     ?errh(T(#{<<"callback_module">> => <<"wow_cool_but_missing">>})),
 1833:     ?errh(T(#{<<"callback_module">> => 1})).
 1834: 
 1835: mod_event_pusher_rabbit(_Config) ->
 1836:     T = fun(Opts) -> #{<<"modules">> =>
 1837:                            #{<<"mod_event_pusher">> =>
 1838:                                  #{<<"backend">> => #{<<"rabbit">> => Opts}}}}
 1839:         end,
 1840:     M = fun(Cfg) -> modopts(mod_event_pusher, [{backends, [{rabbit, Cfg}]}]) end,
 1841:     ?cfgh(M([{presence_exchange, [{name, <<"pres">>}]}]),
 1842:           T(#{<<"presence_exchange">> => #{<<"name">> => <<"pres">>}})),
 1843:     ?cfgh(M([{presence_exchange, [{type, <<"topic">>}]}]),
 1844:           T(#{<<"presence_exchange">> => #{<<"type">> => <<"topic">>}})),
 1845: 
 1846:     %% first two keys are the same as before, test them together
 1847:     ?cfgh(M([{chat_msg_exchange, [{name, <<"pres1">>},
 1848:                                   {type, <<"topic1">>}]}]),
 1849:           T(#{<<"chat_msg_exchange">> => #{<<"name">> => <<"pres1">>,
 1850:                                            <<"type">> => <<"topic1">>}})),
 1851:     ?cfgh(M([{chat_msg_exchange, [{sent_topic, <<"sent_topic1">>}]}]),
 1852:           T(#{<<"chat_msg_exchange">> => #{<<"sent_topic">> => <<"sent_topic1">>}})),
 1853:     ?cfgh(M([{chat_msg_exchange, [{recv_topic, <<"recv_topic1">>}]}]),
 1854:           T(#{<<"chat_msg_exchange">> => #{<<"recv_topic">> => <<"recv_topic1">>}})),
 1855: 
 1856:     %% all keys are the same as before, test them together
 1857:     ?cfgh(M([{groupchat_msg_exchange, [{name, <<"pres2">>},
 1858:                                        {type, <<"topic2">>},
 1859:                                        {sent_topic, <<"sent_topic2">>},
 1860:                                        {recv_topic, <<"recv_topic2">>}]}]),
 1861:           T(#{<<"groupchat_msg_exchange">> => #{<<"name">> => <<"pres2">>,
 1862:                                                 <<"type">> => <<"topic2">>,
 1863:                                                 <<"sent_topic">> => <<"sent_topic2">>,
 1864:                                                 <<"recv_topic">> => <<"recv_topic2">>}})),
 1865: 
 1866:     Exchanges = [<<"presence_exchange">>, <<"chat_msg_exchange">>, <<"groupchat_msg_exchange">>],
 1867:     Keys = [<<"name">>, <<"topic">>, <<"sent_topic">>, <<"recv_topic">>],
 1868:     [?errh(T(#{Exch => #{Key => <<>>}})) || Exch <- Exchanges, Key <- Keys],
 1869:     [?errh(T(#{Exch => #{<<"badkey">> => <<"goodvalue">>}})) || Exch <- Exchanges],
 1870:     ?errh(T(#{<<"money_exchange">> => #{<<"name">> => <<"kantor">>}})).
 1871: 
 1872: mod_http_upload(_Config) ->
 1873:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_http_upload">> => Opts}} end,
 1874:     M = fun(Cfg) -> modopts(mod_http_upload, Cfg) end,
 1875:     RequiredOpts = #{<<"s3">> => http_upload_s3_required_opts()},
 1876:     ExpectedCfg = [{s3, http_upload_s3_expected_cfg()}],
 1877:     ?cfgh(M(ExpectedCfg), T(RequiredOpts)),
 1878:     ?cfgh(M(ExpectedCfg ++ [{host, {prefix, <<"upload.">>}}]),
 1879:           T(RequiredOpts#{<<"host">> => <<"upload.@HOST@">>})),
 1880:     ?cfgh(M(ExpectedCfg ++ [{host, {fqdn, <<"upload.test">>}}]),
 1881:           T(RequiredOpts#{<<"host">> => <<"upload.test">>})),
 1882:     ?cfgh(M(ExpectedCfg ++ [{backend, s3}]),
 1883:           T(RequiredOpts#{<<"backend">> => <<"s3">>})),
 1884:     ?cfgh(M(ExpectedCfg ++ [{expiration_time, 666}]),
 1885:           T(RequiredOpts#{<<"expiration_time">> => 666})),
 1886:     ?cfgh(M(ExpectedCfg ++ [{token_bytes, 32}]),
 1887:           T(RequiredOpts#{<<"token_bytes">> => 32})),
 1888:     ?cfgh(M(ExpectedCfg ++ [{max_file_size, 42}]),
 1889:           T(RequiredOpts#{<<"max_file_size">> => 42})),
 1890:     ?errh(T(#{})), %% missing 's3'
 1891:     ?errh(T(RequiredOpts#{<<"backend">> => <<"">>})),
 1892:     ?errh(T(RequiredOpts#{<<"expiration_time">> => 0})),
 1893:     ?errh(T(RequiredOpts#{<<"token_bytes">> => 0})),
 1894:     ?errh(T(RequiredOpts#{<<"max_file_size">> => 0})),
 1895:     ?errh(T(RequiredOpts#{<<"host">> => <<"is this a host? no.">>})),
 1896:     ?errh(T(RequiredOpts#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
 1897:     ?errh(T(RequiredOpts#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
 1898:     ?errh(T(RequiredOpts#{<<"host">> => [<<"not.supported.any.more.@HOSTS@">>]})),
 1899:     check_iqdisc(mod_http_upload, ExpectedCfg, RequiredOpts).
 1900: 
 1901: mod_http_upload_s3(_Config) ->
 1902:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_http_upload">> =>
 1903:                                               #{<<"s3">> => Opts}}} end,
 1904:     M = fun(Cfg) -> modopts(mod_http_upload, [{s3, Cfg}]) end,
 1905:     RequiredOpts = http_upload_s3_required_opts(),
 1906:     ExpectedCfg = http_upload_s3_expected_cfg(),
 1907:     ?cfgh(M(ExpectedCfg), T(RequiredOpts)),
 1908:     ?cfgh(M(ExpectedCfg ++ [{add_acl, true}]),
 1909:           T(RequiredOpts#{<<"add_acl">> => true})),
 1910:     [?errh(T(maps:remove(Key, RequiredOpts))) || Key <- maps:keys(RequiredOpts)],
 1911:     ?errh(T(RequiredOpts#{<<"bucket_url">> => <<>>})),
 1912:     ?errh(T(RequiredOpts#{<<"region">> => true})),
 1913:     ?errh(T(RequiredOpts#{<<"access_key_id">> => []})),
 1914:     ?errh(T(RequiredOpts#{<<"secret_access_key">> => 3})),
 1915:     ?errh(T(RequiredOpts#{<<"add_acl">> => <<"true">>})).
 1916: 
 1917: http_upload_s3_required_opts() ->
 1918:     #{<<"bucket_url">> => <<"https://s3-eu-west-1.amazonaws.com/mybucket">>,
 1919:       <<"region">> => <<"antarctica-1">>,
 1920:       <<"access_key_id">> => <<"PLEASE">>,
 1921:       <<"secret_access_key">> => <<"ILOVEU">>}.
 1922: 
 1923: http_upload_s3_expected_cfg() ->
 1924:     [{access_key_id, "PLEASE"},
 1925:      {bucket_url, "https://s3-eu-west-1.amazonaws.com/mybucket"},
 1926:      {region, "antarctica-1"},
 1927:      {secret_access_key, "ILOVEU"}].
 1928: 
 1929: mod_jingle_sip(_Config) ->
 1930:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_jingle_sip">> => Opts}} end,
 1931:     M = fun(Cfg) -> modopts(mod_jingle_sip, Cfg) end,
 1932:     ?cfgh(M([{proxy_host, "proxxxy"}]),
 1933:           T(#{<<"proxy_host">> => <<"proxxxy">>})),
 1934:     ?cfgh(M([{proxy_port, 5601}]),
 1935:           T(#{<<"proxy_port">> => 5601})),
 1936:     ?cfgh(M([{listen_port, 5602}]),
 1937:           T(#{<<"listen_port">> => 5602})),
 1938:     ?cfgh(M([{local_host, "localhost"}]),
 1939:           T(#{<<"local_host">> => <<"localhost">>})),
 1940:     ?cfgh(M([{sdp_origin, "127.0.0.1"}]),
 1941:           T(#{<<"sdp_origin">> => <<"127.0.0.1">>})),
 1942:     ?errh(T(#{<<"proxy_host">> => 1})),
 1943:     ?errh(T(#{<<"proxy_port">> => 1000000})),
 1944:     ?errh(T(#{<<"listen_port">> => -1})),
 1945:     ?errh(T(#{<<"local_host">> => <<>>})),
 1946:     ?errh(T(#{<<"sdp_origin">> => <<"abc">>})).
 1947: 
 1948: mod_keystore(_Config) ->
 1949:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_keystore">> => Opts}} end,
 1950:     M = fun(Cfg) -> modopts(mod_keystore, Cfg) end,
 1951:     ?cfgh(M([{ram_key_size, 1024}]),
 1952:           T(#{<<"ram_key_size">> => 1024})),
 1953:     ?errh(T(#{<<"ram_key_size">> => -1})).
 1954: 
 1955: mod_keystore_keys(_Config) ->
 1956:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_keystore">> =>
 1957:                                               #{<<"keys">> => Opts}}}
 1958:         end,
 1959:     M = fun(Cfg) -> modopts(mod_keystore, [{keys, Cfg}]) end,
 1960:     RequiredOpts = #{<<"name">> => <<"access_secret">>,
 1961:                      <<"type">> => <<"ram">>},
 1962:     ?cfgh(M([{access_secret, ram}]),
 1963:           T([RequiredOpts])),
 1964:     ?cfgh(M([{access_secret, {file, "priv/access_psk"}}]),
 1965:           T([RequiredOpts#{<<"type">> => <<"file">>,
 1966:                            <<"path">> => <<"priv/access_psk">>}])),
 1967:     [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)],
 1968:     ?errh(T([RequiredOpts#{<<"name">> => <<>>}])),
 1969:     ?errh(T([RequiredOpts#{<<"type">> => <<"rampampam">>}])),
 1970:     ?errh(T([RequiredOpts#{<<"type">> => <<"file">>}])),
 1971:     ?errh(T([RequiredOpts#{<<"type">> => <<"file">>,
 1972:                            <<"path">> => <<"does/not/exists">>}])).
 1973: 
 1974: mod_last(_Config) ->
 1975:     check_iqdisc(mod_last),
 1976:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_last">> => Opts}} end,
 1977:     M = fun(Cfg) -> modopts(mod_last, Cfg) end,
 1978:     ?cfgh(M([{backend, mnesia}]),
 1979:           T(#{<<"backend">> => <<"mnesia">>})),
 1980:     ?cfgh(M([{bucket_type, <<"test">>}]),
 1981:           T(#{<<"riak">> => #{<<"bucket_type">> => <<"test">>}})),
 1982: 
 1983:     ?errh(T(#{<<"backend">> => <<"frontend">>})),
 1984:     ?errh(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})).
 1985: 
 1986: mod_mam_meta(_Config) ->
 1987:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_mam_meta">> => Opts}} end,
 1988:     M = fun(Cfg) -> modopts(mod_mam_meta, Cfg) end,
 1989:     test_mod_mam_meta(T, M),
 1990:     ?cfgh(M([{bucket_type, <<"mam_bucket">>}]),
 1991:           T(#{<<"riak">> => #{<<"bucket_type">> => <<"mam_bucket">>}})),
 1992:     ?cfgh(M([{search_index, <<"mam_index">>}]),
 1993:           T(#{<<"riak">> => #{<<"search_index">> => <<"mam_index">>}})),
 1994:     ?errh(T(#{<<"riak">> => #{<<"bucket_type">> => <<>>}})),
 1995:     ?errh(T(#{<<"riak">> => #{<<"search_index">> => <<>>}})).
 1996: 
 1997: mod_mam_meta_pm(_Config) ->
 1998:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_mam_meta">> => #{<<"pm">> => Opts}}} end,
 1999:     M = fun(Cfg) -> modopts(mod_mam_meta, [{pm, Cfg}]) end,
 2000:     test_mod_mam_meta(T, M),
 2001:     ?cfgh(M([{archive_groupchats, true}]),
 2002:           T(#{<<"archive_groupchats">> => true})),
 2003:     ?cfgh(M([{same_mam_id_for_peers, true}]),
 2004:           T(#{<<"same_mam_id_for_peers">> => true})),
 2005:     ?errh(T(#{<<"archive_groupchats">> => <<"not really">>})),
 2006:     ?errh(T(#{<<"same_mam_id_for_peers">> => <<"not really">>})).
 2007: 
 2008: mod_mam_meta_muc(_Config) ->
 2009:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_mam_meta">> => #{<<"muc">> => Opts}}} end,
 2010:     M = fun(Cfg) -> modopts(mod_mam_meta, [{muc, Cfg}]) end,
 2011:     test_mod_mam_meta(T, M),
 2012:     ?cfgh(M([{host, {prefix, <<"muc.">>}}]),
 2013:           T(#{<<"host">> => <<"muc.@HOST@">>})),
 2014:     ?cfgh(M([{host, {fqdn, <<"muc.test">>}}]),
 2015:           T(#{<<"host">> => <<"muc.test">>})),
 2016:     ?errh(T(#{<<"host">> => <<"is this a host? no.">>})),
 2017:     ?errh(T(#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
 2018:     ?errh(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
 2019:     ?errh(T(#{<<"archive_groupchats">> => true})),
 2020:     ?errh(T(#{<<"same_mam_id_for_peers">> => true})).
 2021: 
 2022: test_mod_mam_meta(T, M) ->
 2023:     ?cfgh(M([{backend, rdbms}]),
 2024:           T(#{<<"backend">> => <<"rdbms">>})),
 2025:     ?cfgh(M([{no_stanzaid_element, true}]),
 2026:           T(#{<<"no_stanzaid_element">> => true})),
 2027:     ?cfgh(M([{is_archivable_message, mod_mam_utils}]),
 2028:           T(#{<<"is_archivable_message">> => <<"mod_mam_utils">>})),
 2029:     ?cfgh(M([{archive_chat_markers, false}]),
 2030:           T(#{<<"archive_chat_markers">> => false})),
 2031:     ?cfgh(M([{message_retraction, true}]),
 2032:           T(#{<<"message_retraction">> => true})),
 2033:     ?cfgh(M([{cache_users, false}]),
 2034:           T(#{<<"cache_users">> => false})),
 2035:     ?cfgh(M([{rdbms_message_format, simple}]),
 2036:           T(#{<<"rdbms_message_format">> => <<"simple">>})),
 2037:     ?cfgh(M([{async_writer, true}]),
 2038:           T(#{<<"async_writer">> => true})),
 2039:     ?cfgh(M([{flush_interval, 1500}]),
 2040:           T(#{<<"flush_interval">> => 1500})),
 2041:     ?cfgh(M([{max_batch_size, 50}]),
 2042:           T(#{<<"max_batch_size">> => 50})),
 2043:     ?cfgh(M([{pool_size, 50}]),
 2044:           T(#{<<"pool_size">> => 50})),
 2045:     ?cfgh(M([{user_prefs_store, rdbms}]),
 2046:           T(#{<<"user_prefs_store">> => <<"rdbms">>})),
 2047:     ?cfgh(M([{full_text_search, false}]),
 2048:           T(#{<<"full_text_search">> => false})),
 2049:     ?cfgh(M([{default_result_limit, 100}]),
 2050:           T(#{<<"default_result_limit">> => 100})),
 2051:     ?cfgh(M([{max_result_limit, 1000}]),
 2052:           T(#{<<"max_result_limit">> => 1000})),
 2053:     ?cfgh(M([{async_writer_rdbms_pool, async_pool}]),
 2054:           T(#{<<"async_writer_rdbms_pool">> => <<"async_pool">>})),
 2055:     ?cfgh(M([{db_jid_format, mam_jid_rfc}]),
 2056:           T(#{<<"db_jid_format">> => <<"mam_jid_rfc">>})),
 2057:     ?cfgh(M([{db_message_format, mam_message_xml}]),
 2058:           T(#{<<"db_message_format">> => <<"mam_message_xml">>})),
 2059:     ?cfgh(M([{simple, false}]),
 2060:           T(#{<<"simple">> => false})),
 2061:     ?cfgh(M([{extra_fin_element, mod_mam_utils}]),
 2062:           T(#{<<"extra_fin_element">> => <<"mod_mam_utils">>})),
 2063:     ?cfgh(M([{extra_lookup_params, mod_mam_utils}]),
 2064:           T(#{<<"extra_lookup_params">> => <<"mod_mam_utils">>})),
 2065:     ?cfgh(M([{cache, [{module, internal}]}]),
 2066:           T(#{<<"cache">> => #{<<"module">> => <<"internal">>}})),
 2067:     ?cfgh(M([{cache, [{time_to_live, 8600}]}]),
 2068:           T(#{<<"cache">> => #{<<"time_to_live">> => 8600}})),
 2069:     ?cfgh(M([{cache, [{time_to_live, infinity}]}]),
 2070:           T(#{<<"cache">> => #{<<"time_to_live">> => <<"infinity">>}})),
 2071:     ?cfgh(M([{cache, [{number_of_segments, 10}]}]),
 2072:           T(#{<<"cache">> => #{<<"number_of_segments">> => 10}})),
 2073:     ?cfgh(M([{cache, [{strategy, fifo}]}]),
 2074:           T(#{<<"cache">> => #{<<"strategy">> => <<"fifo">>}})),
 2075:     ?errh(T(#{<<"backend">> => <<"notepad">>})),
 2076:     ?errh(T(#{<<"no_stanzaid_element">> => <<"true">>})),
 2077:     ?errh(T(#{<<"is_archivable_message">> => <<"mod_mam_fake">>})),
 2078:     ?errh(T(#{<<"archive_chat_markers">> => <<"maybe">>})),
 2079:     ?errh(T(#{<<"message_retraction">> => 1})),
 2080:     ?errh(T(#{<<"cache_users">> => []})),
 2081:     ?errh(T(#{<<"rdbms_message_format">> => <<"complex">>})),
 2082:     ?errh(T(#{<<"async_writer">> => #{}})),
 2083:     ?errh(T(#{<<"flush_interval">> => -1})),
 2084:     ?errh(T(#{<<"max_batch_size">> => -1})),
 2085:     ?errh(T(#{<<"pool_size">> => -1})),
 2086:     ?errh(T(#{<<"user_prefs_store">> => <<"textfile">>})),
 2087:     ?errh(T(#{<<"full_text_search">> => <<"disabled">>})),
 2088:     ?errh(T(#{<<"default_result_limit">> => -1})),
 2089:     ?errh(T(#{<<"max_result_limit">> => -2})),
 2090:     ?errh(T(#{<<"async_writer_rdbms_pool">> => <<>>})),
 2091:     ?errh(T(#{<<"db_jid_format">> => <<"not_a_module">>})),
 2092:     ?errh(T(#{<<"db_message_format">> => <<"not_a_module">>})),
 2093:     ?errh(T(#{<<"simple">> => <<"yes">>})),
 2094:     ?errh(T(#{<<"extra_fin_element">> => <<"bad_module">>})),
 2095:     ?errh(T(#{<<"extra_lookup_params">> => <<"bad_module">>})),
 2096:     ?errh(T(#{<<"cache">> => #{<<"module">> => <<"mod_wrong_cache">>}})),
 2097:     ?errh(T(#{<<"cache">> => #{<<"module">> => <<"mod_cache_users">>,
 2098:                                <<"time_to_live">> => 8600}})),
 2099:     ?errh(T(#{<<"cache">> => #{<<"time_to_live">> => 0}})),
 2100:     ?errh(T(#{<<"cache">> => #{<<"strategy">> => <<"lifo">>}})),
 2101:     ?errh(T(#{<<"cache">> => #{<<"number_of_segments">> => 0}})),
 2102:     ?errh(T(#{<<"cache">> => #{<<"number_of_segments">> => <<"infinity">>}})).
 2103: 
 2104: mod_muc(_Config) ->
 2105:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc">> => Opts}} end,
 2106:     M = fun(Cfg) -> modopts(mod_muc, Cfg) end,
 2107:     ?cfgh(M([{host, {prefix, <<"conference.">>}}]),
 2108:           T(#{<<"host">> => <<"conference.@HOST@">>})),
 2109:     ?cfgh(M([{host, {fqdn, <<"conference.test">>}}]),
 2110:           T(#{<<"host">> => <<"conference.test">>})),
 2111:     ?cfgh(M([{backend, mnesia}]),
 2112:           T(#{<<"backend">> => <<"mnesia">>})),
 2113:     ?cfgh(M([{access, all}]),
 2114:           T(#{<<"access">> => <<"all">>})),
 2115:     ?cfgh(M([{access_create, admin}]),
 2116:           T(#{<<"access_create">> => <<"admin">>})),
 2117:     ?cfgh(M([{access_admin, none}]),
 2118:           T(#{<<"access_admin">> => <<"none">>})),
 2119:     ?cfgh(M([{access_persistent, all}]),
 2120:           T(#{<<"access_persistent">> => <<"all">>})),
 2121:     ?cfgh(M([{history_size, 20}]),
 2122:           T(#{<<"history_size">> => 20})),
 2123:     ?cfgh(M([{room_shaper, muc_room_shaper}]),
 2124:           T(#{<<"room_shaper">> => <<"muc_room_shaper">>})),
 2125:     ?cfgh(M([{max_room_id, infinity}]),
 2126:           T(#{<<"max_room_id">> => <<"infinity">>})),
 2127:     ?cfgh(M([{max_room_name, 30}]),
 2128:           T(#{<<"max_room_name">> => 30})),
 2129:     ?cfgh(M([{max_room_desc, 0}]),
 2130:           T(#{<<"max_room_desc">> => 0})),
 2131:     ?cfgh(M([{min_message_interval, 10}]),
 2132:           T(#{<<"min_message_interval">> => 10})),
 2133:     ?cfgh(M([{min_presence_interval, 0}]),
 2134:           T(#{<<"min_presence_interval">> => 0})),
 2135:     ?cfgh(M([{max_users, 30}]),
 2136:           T(#{<<"max_users">> => 30})),
 2137:     ?cfgh(M([{max_users_admin_threshold, 2}]),
 2138:           T(#{<<"max_users_admin_threshold">> => 2})),
 2139:     ?cfgh(M([{user_message_shaper, muc_msg_shaper}]),
 2140:           T(#{<<"user_message_shaper">> => <<"muc_msg_shaper">>})),
 2141:     ?cfgh(M([{user_presence_shaper, muc_pres_shaper}]),
 2142:           T(#{<<"user_presence_shaper">> => <<"muc_pres_shaper">>})),
 2143:     ?cfgh(M([{max_user_conferences, 10}]),
 2144:           T(#{<<"max_user_conferences">> => 10})),
 2145:     ?cfgh(M([{http_auth_pool, external_auth}]),
 2146:           T(#{<<"http_auth_pool">> => <<"external_auth">>})),
 2147:     ?cfgh(M([{load_permanent_rooms_at_startup, true}]),
 2148:           T(#{<<"load_permanent_rooms_at_startup">> => true})),
 2149:     ?cfgh(M([{hibernate_timeout, infinity}]),
 2150:           T(#{<<"hibernate_timeout">> => <<"infinity">>})),
 2151:     ?cfgh(M([{hibernated_room_check_interval, 5000}]),
 2152:           T(#{<<"hibernated_room_check_interval">> => 5000})),
 2153:     ?cfgh(M([{hibernated_room_timeout, 0}]),
 2154:           T(#{<<"hibernated_room_timeout">> => 0})),
 2155:     ?errh(T(#{<<"host">> => <<>>})),
 2156:     ?errh(T(#{<<"host">> => <<"is this a host? no.">>})),
 2157:     ?errh(T(#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
 2158:     ?errh(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
 2159:     ?errh(T(#{<<"backend">> => <<"amnesia">>})),
 2160:     ?errh(T(#{<<"access">> => <<>>})),
 2161:     ?errh(T(#{<<"access_create">> => 1})),
 2162:     ?errh(T(#{<<"access_admin">> => []})),
 2163:     ?errh(T(#{<<"access_persistent">> => true})),
 2164:     ?errh(T(#{<<"history_size">> => <<"20">>})),
 2165:     ?errh(T(#{<<"room_shaper">> => <<>>})),
 2166:     ?errh(T(#{<<"max_room_id">> => #{}})),
 2167:     ?errh(T(#{<<"max_room_name">> => <<"infinite!">>})),
 2168:     ?errh(T(#{<<"max_room_desc">> => -1})),
 2169:     ?errh(T(#{<<"min_message_interval">> => -10})),
 2170:     ?errh(T(#{<<"min_presence_interval">> => <<"infinity">>})),
 2171:     ?errh(T(#{<<"max_users">> => 0})),
 2172:     ?errh(T(#{<<"max_users_admin_threshold">> => 0})),
 2173:     ?errh(T(#{<<"user_message_shaper">> => []})),
 2174:     ?errh(T(#{<<"user_presence_shaper">> => <<>>})),
 2175:     ?errh(T(#{<<"max_user_conferences">> => -1})),
 2176:     ?errh(T(#{<<"http_auth_pool">> => <<>>})),
 2177:     ?errh(T(#{<<"load_permanent_rooms_at_startup">> => <<"true">>})),
 2178:     ?errh(T(#{<<"hibernate_timeout">> => <<"really big">>})),
 2179:     ?errh(T(#{<<"hibernated_room_check_interval">> => -1})),
 2180:     ?errh(T(#{<<"hibernated_room_timeout">> => false})).
 2181: 
 2182: mod_muc_default_room(_Config) ->
 2183:     T = fun(Opts) -> #{<<"modules">> =>
 2184:                            #{<<"mod_muc">> => #{<<"default_room">> => Opts}}} end,
 2185:     M = fun(Cfg) -> modopts(mod_muc, [{default_room_options, Cfg}]) end,
 2186:     ?cfgh(M([]), T(#{})),
 2187:     ?cfgh(M([{title, <<"living room">>}]),
 2188:           T(#{<<"title">> => <<"living room">>})),
 2189:     ?cfgh(M([{description, <<"a room that is alive">>}]),
 2190:           T(#{<<"description">> => <<"a room that is alive">>})),
 2191:     ?cfgh(M([{allow_change_subj, true}]),
 2192:           T(#{<<"allow_change_subj">> => true})),
 2193:     ?cfgh(M([{allow_query_users, false}]),
 2194:           T(#{<<"allow_query_users">> => false})),
 2195:     ?cfgh(M([{allow_private_messages, true}]),
 2196:           T(#{<<"allow_private_messages">> => true})),
 2197:     ?cfgh(M([{allow_visitor_status, false}]),
 2198:           T(#{<<"allow_visitor_status">> => false})),
 2199:     ?cfgh(M([{allow_visitor_nickchange, true}]),
 2200:           T(#{<<"allow_visitor_nickchange">> => true})),
 2201:     ?cfgh(M([{public, false}]),
 2202:           T(#{<<"public">> => false})),
 2203:     ?cfgh(M([{public_list, true}]),
 2204:           T(#{<<"public_list">> => true})),
 2205:     ?cfgh(M([{persistent, true}]),
 2206:           T(#{<<"persistent">> => true})),
 2207:     ?cfgh(M([{moderated, false}]),
 2208:           T(#{<<"moderated">> => false})),
 2209:     ?cfgh(M([{members_by_default, true}]),
 2210:           T(#{<<"members_by_default">> => true})),
 2211:     ?cfgh(M([{members_only, false}]),
 2212:           T(#{<<"members_only">> => false})),
 2213:     ?cfgh(M([{allow_user_invites, true}]),
 2214:           T(#{<<"allow_user_invites">> => true})),
 2215:     ?cfgh(M([{allow_multiple_sessions, false}]),
 2216:           T(#{<<"allow_multiple_sessions">> => false})),
 2217:     ?cfgh(M([{password_protected, true}]),
 2218:           T(#{<<"password_protected">> => true})),
 2219:     ?cfgh(M([{password, <<"secret">>}]),
 2220:           T(#{<<"password">> => <<"secret">>})),
 2221:     ?cfgh(M([{anonymous, true}]),
 2222:           T(#{<<"anonymous">> => true})),
 2223:     ?cfgh(M([{max_users, 100}]),
 2224:           T(#{<<"max_users">> => 100})),
 2225:     ?cfgh(M([{logging, false}]),
 2226:           T(#{<<"logging">> => false})),
 2227:     ?cfgh(M([{maygetmemberlist, [moderator]}]),
 2228:           T(#{<<"maygetmemberlist">> => [<<"moderator">>]})),
 2229:     ?cfgh(M([{subject, <<"Lambda days">>}]),
 2230:           T(#{<<"subject">> => <<"Lambda days">>})),
 2231:     ?cfgh(M([{subject_author, <<"Alice">>}]),
 2232:           T(#{<<"subject_author">> => <<"Alice">>})),
 2233:     ?errh(T(<<"bad value">>)),
 2234:     ?errh(T(#{<<"title">> => true})),
 2235:     ?errh(T(#{<<"description">> => 1})),
 2236:     ?errh(T(#{<<"allow_change_subj">> => <<"true">>})),
 2237:     ?errh(T(#{<<"allow_query_users">> => <<>>})),
 2238:     ?errh(T(#{<<"allow_private_messages">> => 1})),
 2239:     ?errh(T(#{<<"allow_visitor_status">> => []})),
 2240:     ?errh(T(#{<<"allow_visitor_nickchange">> => #{}})),
 2241:     ?errh(T(#{<<"public">> => 0})),
 2242:     ?errh(T(#{<<"public_list">> => [false]})),
 2243:     ?errh(T(#{<<"persistent">> => 1})),
 2244:     ?errh(T(#{<<"moderated">> => <<"yes">>})),
 2245:     ?errh(T(#{<<"members_by_default">> => 0})),
 2246:     ?errh(T(#{<<"members_only">> => [true]})),
 2247:     ?errh(T(#{<<"allow_user_invites">> => <<>>})),
 2248:     ?errh(T(#{<<"allow_multiple_sessions">> => []})),
 2249:     ?errh(T(#{<<"password_protected">> => #{}})),
 2250:     ?errh(T(#{<<"password">> => false})),
 2251:     ?errh(T(#{<<"anonymous">> => <<"maybe">>})),
 2252:     ?errh(T(#{<<"max_users">> => 0})),
 2253:     ?errh(T(#{<<"logging">> => [true, false]})),
 2254:     ?errh(T(#{<<"maygetmemberlist">> => <<"moderator">>})),
 2255:     ?errh(T(#{<<"maygetmemberlist">> => [<<>>]})),
 2256:     ?errh(T(#{<<"subject">> => [<<"subjective">>]})),
 2257:     ?errh(T(#{<<"subject_author">> => 1})).
 2258: 
 2259: mod_muc_default_room_affiliations(_Config) ->
 2260:     T = fun(Opts) -> #{<<"modules">> =>
 2261:                            #{<<"mod_muc">> =>
 2262:                                  #{<<"default_room">> => #{<<"affiliations">> => Opts}}}} end,
 2263:     M = fun(Cfg) -> modopts(mod_muc, [{default_room_options, [{affiliations, Cfg}]}]) end,
 2264:     RequiredOpts = #{<<"user">> => <<"alice">>,
 2265:                      <<"server">> => <<"localhost">>,
 2266:                      <<"resource">> => <<"phone">>,
 2267:                      <<"affiliation">> => <<"moderator">>},
 2268:     ExpectedCfg = {{<<"alice">>, <<"localhost">>, <<"phone">>}, moderator},
 2269:     ?cfgh(M([]), T([])),
 2270:     ?cfgh(M([ExpectedCfg]), T([RequiredOpts])),
 2271:     [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)],
 2272:     ?errh(T([RequiredOpts#{<<"user">> := <<>>}])),
 2273:     ?errh(T([RequiredOpts#{<<"server">> := <<"domain? not really!">>}])),
 2274:     ?errh(T([RequiredOpts#{<<"resource">> := false}])),
 2275:     ?errh(T([RequiredOpts#{<<"affiliation">> := <<>>}])).
 2276: 
 2277: mod_muc_log(_Config) ->
 2278:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc_log">> => Opts}} end,
 2279:     M = fun(Cfg) -> modopts(mod_muc_log, Cfg) end,
 2280:     ?cfgh(M([{outdir, "www/muc"}]),
 2281:           T(#{<<"outdir">> => <<"www/muc">>})),
 2282:     ?cfgh(M([{access_log, muc_admin}]),
 2283:           T(#{<<"access_log">> => <<"muc_admin">>})),
 2284:     ?cfgh(M([{dirtype, subdirs}]),
 2285:           T(#{<<"dirtype">> => <<"subdirs">>})),
 2286:     ?cfgh(M([{dirname, room_name}]),
 2287:           T(#{<<"dirname">> => <<"room_name">>})),
 2288:     ?cfgh(M([{file_format, html}]),
 2289:           T(#{<<"file_format">> => <<"html">>})),
 2290:     ?cfgh(M([{cssfile, <<"path/to/css_file">>}]),
 2291:           T(#{<<"css_file">> => <<"path/to/css_file">>})),
 2292:     ?cfgh(M([{timezone, local}]),
 2293:           T(#{<<"timezone">> => <<"local">>})),
 2294:     ?cfgh(M([{spam_prevention, false}]),
 2295:           T(#{<<"spam_prevention">> => false})),
 2296:     ?errh(T(#{<<"outdir">> => <<"does/not/exist">>})),
 2297:     ?errh(T(#{<<"access_log">> => 1})),
 2298:     ?errh(T(#{<<"dirtype">> => <<"imaginary">>})),
 2299:     ?errh(T(#{<<"dirname">> => <<"dyrektory">>})),
 2300:     ?errh(T(#{<<"file_format">> => <<"none">>})),
 2301:     ?errh(T(#{<<"css_file">> => <<>>})),
 2302:     ?errh(T(#{<<"timezone">> => <<"yes">>})),
 2303:     ?errh(T(#{<<"spam_prevention">> => <<"spam and eggs and spam">>})).
 2304: 
 2305: mod_muc_log_top_link(_Config) ->
 2306:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc_log">> => #{<<"top_link">> => Opts}}} end,
 2307:     M = fun(Cfg) -> modopts(mod_muc_log, [{top_link, Cfg}]) end,
 2308:     RequiredOpts = #{<<"target">> => <<"https://esl.github.io/MongooseDocs/">>,
 2309:                      <<"text">> => <<"Docs">>},
 2310:     ExpectedCfg = {"https://esl.github.io/MongooseDocs/", "Docs"},
 2311:     ?cfgh(M(ExpectedCfg), T(RequiredOpts)),
 2312:     [?errh(T(maps:remove(K, RequiredOpts))) || K <- maps:keys(RequiredOpts)],
 2313:     ?errh(T(RequiredOpts#{<<"target">> => true})),
 2314:     ?errh(T(RequiredOpts#{<<"text">> => <<"">>})).
 2315: 
 2316: mod_muc_light(_Config) ->
 2317:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_muc_light">> => Opts}} end,
 2318:     M = fun(Cfg) -> modopts(mod_muc_light, Cfg) end,
 2319:     ?cfgh(M([{backend, mnesia}]),
 2320:           T(#{<<"backend">> => <<"mnesia">>})),
 2321:     ?cfgh(M([{host, {prefix, <<"muclight.">>}}]),
 2322:           T(#{<<"host">> => <<"muclight.@HOST@">>})),
 2323:     ?cfgh(M([{host, {fqdn, <<"muclight.test">>}}]),
 2324:           T(#{<<"host">> => <<"muclight.test">>})),
 2325:     ?cfgh(M([{equal_occupants, true}]),
 2326:           T(#{<<"equal_occupants">> => true})),
 2327:     ?cfgh(M([{legacy_mode, false}]),
 2328:           T(#{<<"legacy_mode">> => false})),
 2329:     ?cfgh(M([{rooms_per_user, 100}]),
 2330:           T(#{<<"rooms_per_user">> => 100})),
 2331:     ?cfgh(M([{blocking, false}]),
 2332:           T(#{<<"blocking">> => false})),
 2333:     ?cfgh(M([{all_can_configure, true}]),
 2334:           T(#{<<"all_can_configure">> => true})),
 2335:     ?cfgh(M([{all_can_invite, false}]),
 2336:           T(#{<<"all_can_invite">> => false})),
 2337:     ?cfgh(M([{max_occupants, infinity}]),
 2338:           T(#{<<"max_occupants">> => <<"infinity">>})),
 2339:     ?cfgh(M([{rooms_per_page, 10}]),
 2340:           T(#{<<"rooms_per_page">> => 10})),
 2341:     ?cfgh(M([{rooms_in_rosters, true}]),
 2342:           T(#{<<"rooms_in_rosters">> => true})),
 2343:     ?errh(T(#{<<"backend">> => <<"frontend">>})),
 2344:     ?errh(T(#{<<"host">> => <<"what is a domain?!">>})),
 2345:     ?errh(T(#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
 2346:     ?errh(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
 2347:     ?errh(T(#{<<"equal_occupants">> => <<"true">>})),
 2348:     ?errh(T(#{<<"legacy_mode">> => 1234})),
 2349:     ?errh(T(#{<<"rooms_per_user">> => 0})),
 2350:     ?errh(T(#{<<"blocking">> => <<"true">>})),
 2351:     ?errh(T(#{<<"all_can_configure">> => []})),
 2352:     ?errh(T(#{<<"all_can_invite">> => #{}})),
 2353:     ?errh(T(#{<<"max_occupants">> => <<"seven">>})),
 2354:     ?errh(T(#{<<"rooms_per_page">> => false})),
 2355:     ?errh(T(#{<<"rooms_in_rosters">> => [1, 2, 3]})).
 2356: 
 2357: mod_muc_light_config_schema(_Config) ->
 2358:     T = fun(Opts) -> #{<<"modules">> =>
 2359:                            #{<<"mod_muc_light">> => #{<<"config_schema">> => Opts}}} end,
 2360:     M = fun(Cfg) -> modopts(mod_muc_light, [{config_schema, Cfg}]) end,
 2361:     Field = #{<<"field">> => <<"my_field">>},
 2362:     ?cfgh(M([]), T([])),
 2363:     ?cfgh(M([{<<"my_field">>, <<"My Room">>, my_field, binary}]),
 2364:           T([Field#{<<"string_value">> => <<"My Room">>}])),
 2365:     ?cfgh(M([{<<"my_field">>, 1, my_field, integer}]),
 2366:           T([Field#{<<"integer_value">> => 1}])),
 2367:     ?cfgh(M([{<<"my_field">>, 0.5, my_field, float}]),
 2368:           T([Field#{<<"float_value">> => 0.5}])),
 2369:     ?cfgh(M([{<<"my_field">>, 0, your_field, integer}]),
 2370:           T([Field#{<<"integer_value">> => 0,
 2371:                     <<"internal_key">> => <<"your_field">>}])),
 2372:     ?cfgh(M([{<<"żółć"/utf8>>, <<"Рентгеноэлектрокардиографический"/utf8>>, 'żółć', binary}]),
 2373:           T([#{<<"field">> => <<"żółć"/utf8>>,
 2374:                <<"string_value">> => <<"Рентгеноэлектрокардиографический"/utf8>>}])),
 2375:     ?cfgh(M([{<<"first">>, 1, first, integer}, % the config is u-key-sorted
 2376:              {<<"second">>, <<"two">>, second, binary}]),
 2377:           T([#{<<"field">> => <<"second">>, <<"string_value">> => <<"two">>},
 2378:              #{<<"field">> => <<"second">>, <<"float_value">> => 2.0},
 2379:              #{<<"field">> => <<"first">>, <<"integer_value">> => 1}])),
 2380:     ?errh(T([#{<<"string_value">> => <<"My Room">>}])),
 2381:     ?errh(T([#{<<"field">> => <<>>,
 2382:                <<"string_value">> => <<"My Room">>}])),
 2383:     ?errh(T([Field#{<<"string_value">> => 0}])),
 2384:     ?errh(T([Field#{<<"integer_value">> => 1.5}])),
 2385:     ?errh(T([Field#{<<"float_value">> => 1}])),
 2386:     ?errh(T([Field#{<<"integer_value">> => 0,
 2387:                     <<"string_value">> => <<"My Room">>}])),
 2388:     ?errh(T([Field#{<<"integer_value">> => 0,
 2389:                     <<"internal_key">> => <<>>}])).
 2390: 
 2391: mod_offline(_Config) ->
 2392:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_offline">> => Opts}} end,
 2393:     M = fun(Cfg) -> modopts(mod_offline, Cfg) end,
 2394:     ?cfgh(M([{access_max_user_messages, max_user_offline_messages}]),
 2395:           T(#{<<"access_max_user_messages">> => <<"max_user_offline_messages">>})),
 2396:     ?cfgh(M([{backend, rdbms}]),
 2397:           T(#{<<"backend">> => <<"rdbms">>})),
 2398:     ?cfgh(M([{bucket_type, <<"test">>}]),
 2399:           T(#{<<"riak">> => #{<<"bucket_type">> => <<"test">>}})),
 2400:     ?errh(T(#{<<"access_max_user_messages">> => 1})),
 2401:     ?errh(T(#{<<"backend">> => <<"riak_is_the_best">>})),
 2402:     ?errh(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})),
 2403:     ?errh(T(#{<<"riak">> => #{<<"bucket">> => <<"leaky">>}})).
 2404: 
 2405: mod_ping(_Config) ->
 2406:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_ping">> => Opts}} end,
 2407:     M = fun(Cfg) -> modopts(mod_ping, Cfg) end,
 2408:     ?cfgh(M([{send_pings, true}]),
 2409:           T(#{<<"send_pings">> => true})),
 2410:     ?cfgh(M([{ping_interval, timer:seconds(10)}]),
 2411:           T(#{<<"ping_interval">> => 10})),
 2412:     ?cfgh(M([{timeout_action, kill}]),
 2413:           T(#{<<"timeout_action">> => <<"kill">>})),
 2414:     ?cfgh(M([{ping_req_timeout, timer:seconds(20)}]),
 2415:           T(#{<<"ping_req_timeout">> => 20})),
 2416:     ?errh(T(#{<<"send_pings">> => 1})),
 2417:     ?errh(T(#{<<"ping_interval">> => 0})),
 2418:     ?errh(T(#{<<"timeout_action">> => <<"kill_them_all">>})),
 2419:     ?errh(T(#{<<"ping_req_timeout">> => 0})),
 2420:     check_iqdisc(mod_ping).
 2421: 
 2422: mod_privacy(_Config) ->
 2423:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_privacy">> => Opts}} end,
 2424:     M = fun(Cfg) -> modopts(mod_privacy, Cfg) end,
 2425:     ?cfgh(M([{backend, mnesia}]),
 2426:           T(#{<<"backend">> => <<"mnesia">>})),
 2427:     ?cfgh(M([{defaults_bucket_type, <<"defaults">>}]),
 2428:           T(#{<<"riak">> => #{<<"defaults_bucket_type">> => <<"defaults">>}})),
 2429:     ?cfgh(M([{names_bucket_type, <<"names">>}]),
 2430:           T(#{<<"riak">> => #{<<"names_bucket_type">> => <<"names">>}})),
 2431:     ?cfgh(M([{bucket_type, <<"bucket">>}]),
 2432:           T(#{<<"riak">> => #{<<"bucket_type">> => <<"bucket">>}})),
 2433:     ?errh(T(#{<<"backend">> => <<"mongoddt">>})),
 2434:     ?errh(T(#{<<"riak">> => #{<<"defaults_bucket_type">> => <<>>}})),
 2435:     ?errh(T(#{<<"riak">> => #{<<"names_bucket_type">> => 1}})),
 2436:     ?errh(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})).
 2437: 
 2438: mod_private(_Config) ->
 2439:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_private">> => Opts}} end,
 2440:     M = fun(Cfg) -> modopts(mod_private, Cfg) end,
 2441:     ?cfgh(M([{backend, riak}]),
 2442:           T(#{<<"backend">> => <<"riak">>})),
 2443:     ?cfgh(M([{bucket_type, <<"private_stuff">>}]),
 2444:           T(#{<<"riak">> => #{<<"bucket_type">> => <<"private_stuff">>}})),
 2445:     ?errh(T(#{<<"backend">> => <<"mssql">>})),
 2446:     ?errh(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})),
 2447:     check_iqdisc(mod_private).
 2448: 
 2449: mod_pubsub(_Config) ->
 2450:     check_iqdisc(mod_pubsub),
 2451:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_pubsub">> => Opts}} end,
 2452:     M = fun(Cfg) -> modopts(mod_pubsub, Cfg) end,
 2453:     ?cfgh(M([{host, {prefix, <<"pubsub.">>}}]),
 2454:           T(#{<<"host">> => <<"pubsub.@HOST@">>})),
 2455:     ?cfgh(M([{host, {fqdn, <<"pubsub.test">>}}]),
 2456:           T(#{<<"host">> => <<"pubsub.test">>})),
 2457:     ?cfgh(M([{backend, rdbms}]),
 2458:           T(#{<<"backend">> => <<"rdbms">>})),
 2459:     ?cfgh(M([{access_createnode, all}]),
 2460:           T(#{<<"access_createnode">> => <<"all">>})),
 2461:     ?cfgh(M([{max_items_node, 20}]),
 2462:           T(#{<<"max_items_node">> => 20})),
 2463:     ?cfgh(M([{max_subscriptions_node, 30}]),
 2464:           T(#{<<"max_subscriptions_node">> => 30})),
 2465:     ?cfgh(M([{nodetree, <<"tree">>}]),
 2466:           T(#{<<"nodetree">> => <<"tree">>})),
 2467:     ?cfgh(M([{ignore_pep_from_offline, false}]),
 2468:           T(#{<<"ignore_pep_from_offline">> => false})),
 2469:     ?cfgh(M([{last_item_cache, rdbms}]),
 2470:           T(#{<<"last_item_cache">> => <<"rdbms">>})),
 2471:     ?cfgh(M([{plugins, [<<"flat">>, <<"dag">>]}]),
 2472:           T(#{<<"plugins">> => [<<"flat">>, <<"dag">>]})),
 2473:     ?cfgh(M([{item_publisher, true}]),
 2474:           T(#{<<"item_publisher">> => true})),
 2475:     ?cfgh(M([{sync_broadcast, false}]),
 2476:           T(#{<<"sync_broadcast">> => false})),
 2477:     ?errh(T(#{<<"host">> => <<"">>})),
 2478:     ?errh(T(#{<<"host">> => <<"is this a host? no.">>})),
 2479:     ?errh(T(#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
 2480:     ?errh(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
 2481:     ?errh(T(#{<<"backend">> => <<"amnesia">>})),
 2482:     ?errh(T(#{<<"access_createnode">> => <<"">>})),
 2483:     ?errh(T(#{<<"max_items_node">> => -1})),
 2484:     ?errh(T(#{<<"max_subscriptions_node">> => 3.1415})),
 2485:     ?errh(T(#{<<"nodetree">> => <<"christmas_tree">>})),
 2486:     ?errh(T(#{<<"ignore_pep_from_offline">> => <<"maybe">>})),
 2487:     ?errh(T(#{<<"last_item_cache">> => false})),
 2488:     ?errh(T(#{<<"plugins">> => [<<"deep">>]})),
 2489:     ?errh(T(#{<<"item_publisher">> => 1})),
 2490:     ?errh(T(#{<<"sync_broadcast">> => []})).
 2491: 
 2492: mod_pubsub_pep_mapping(_Config) ->
 2493:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_pubsub">> =>
 2494:                                               #{<<"pep_mapping">> => Opts}}} end,
 2495:     M = fun(Cfg) -> modopts(mod_pubsub, [{pep_mapping, Cfg}]) end,
 2496:     RequiredOpts = #{<<"namespace">> => <<"urn:xmpp:microblog:0">>,
 2497:                      <<"node">> => <<"mb">>},
 2498:     ?cfgh(M([{<<"urn:xmpp:microblog:0">>, <<"mb">>}]),
 2499:           T([RequiredOpts])),
 2500:     [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)],
 2501:     [?errh(T([RequiredOpts#{Key => <<>>}])) || Key <- maps:keys(RequiredOpts)].
 2502: 
 2503: mod_pubsub_default_node_config(_Config) ->
 2504:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_pubsub">> =>
 2505:                                               #{<<"default_node_config">> => Opts}}} end,
 2506:     M = fun(Cfg) -> modopts(mod_pubsub, [{default_node_config, Cfg}]) end,
 2507:     ?cfgh(M([{access_model, open}]),
 2508:           T(#{<<"access_model">> => <<"open">>})),
 2509:     ?cfgh(M([{deliver_notifications, true}]),
 2510:           T(#{<<"deliver_notifications">> => true})),
 2511:     ?cfgh(M([{deliver_payloads, false}]),
 2512:           T(#{<<"deliver_payloads">> => false})),
 2513:     ?cfgh(M([{max_items, 1000}]),
 2514:           T(#{<<"max_items">> => 1000})),
 2515:     ?cfgh(M([{max_payload_size, 1000}]),
 2516:           T(#{<<"max_payload_size">> => 1000})),
 2517:     ?cfgh(M([{node_type, dag}]),
 2518:           T(#{<<"node_type">> => <<"dag">>})),
 2519:     ?cfgh(M([{notification_type, headline}]),
 2520:           T(#{<<"notification_type">> => <<"headline">>})),
 2521:     ?cfgh(M([{notify_config, true}]),
 2522:           T(#{<<"notify_config">> => true})),
 2523:     ?cfgh(M([{notify_delete, false}]),
 2524:           T(#{<<"notify_delete">> => false})),
 2525:     ?cfgh(M([{notify_retract, true}]),
 2526:           T(#{<<"notify_retract">> => true})),
 2527:     ?cfgh(M([{persist_items, false}]),
 2528:           T(#{<<"persist_items">> => false})),
 2529:     ?cfgh(M([{presence_based_delivery, true}]),
 2530:           T(#{<<"presence_based_delivery">> => true})),
 2531:     ?cfgh(M([{publish_model, open}]),
 2532:           T(#{<<"publish_model">> => <<"open">>})),
 2533:     ?cfgh(M([{purge_offline, false}]),
 2534:           T(#{<<"purge_offline">> => false})),
 2535:     ?cfgh(M([{roster_groups_allowed, [<<"friends">>]}]),
 2536:           T(#{<<"roster_groups_allowed">> => [<<"friends">>]})),
 2537:     ?cfgh(M([{send_last_published_item, on_sub_and_presence}]),
 2538:           T(#{<<"send_last_published_item">> => <<"on_sub_and_presence">>})),
 2539:     ?cfgh(M([{subscribe, true}]),
 2540:           T(#{<<"subscribe">> => true})),
 2541:     ?errh(T(#{<<"access_model">> => <<>>})),
 2542:     ?errh(T(#{<<"deliver_notifications">> => <<"yes">>})),
 2543:     ?errh(T(#{<<"deliver_payloads">> => 0})),
 2544:     ?errh(T(#{<<"max_items">> => -1})),
 2545:     ?errh(T(#{<<"max_payload_size">> => -1})),
 2546:     ?errh(T(#{<<"node_type">> => [<<"dag">>]})),
 2547:     ?errh(T(#{<<"notification_type">> => <<>>})),
 2548:     ?errh(T(#{<<"notify_config">> => <<"false">>})),
 2549:     ?errh(T(#{<<"notify_delete">> => [true]})),
 2550:     ?errh(T(#{<<"notify_retract">> => #{}})),
 2551:     ?errh(T(#{<<"persist_items">> => 1})),
 2552:     ?errh(T(#{<<"presence_based_delivery">> => []})),
 2553:     ?errh(T(#{<<"publish_model">> => <<"">>})),
 2554:     ?errh(T(#{<<"purge_offline">> => 1})),
 2555:     ?errh(T(#{<<"roster_groups_allowed">> => [<<>>]})),
 2556:     ?errh(T(#{<<"send_last_published_item">> => <<>>})),
 2557:     ?errh(T(#{<<"subscribe">> => <<"never">>})).
 2558: 
 2559: mod_push_service_mongoosepush(_Config) ->
 2560:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_push_service_mongoosepush">> => Opts}} end,
 2561:     M = fun(Cfg) -> modopts(mod_push_service_mongoosepush, Cfg) end,
 2562:     ?cfgh(M([{pool_name, test_pool}]),
 2563:           T(#{<<"pool_name">> => <<"test_pool">>})),
 2564:     ?cfgh(M([{api_version, "v3"}]),
 2565:           T(#{<<"api_version">> => <<"v3">>})),
 2566:     ?cfgh(M([{max_http_connections, 100}]),
 2567:           T(#{<<"max_http_connections">> => 100})),
 2568:     ?errh(T(#{<<"pool_name">> => 1})),
 2569:     ?errh(T(#{<<"api_version">> => <<"v4">>})),
 2570:     ?errh(T(#{<<"max_http_connections">> => -1})).
 2571: 
 2572: mod_register(_Config) ->
 2573:     ?cfgh(modopts(mod_register, [{access,register},
 2574:                                  {ip_access, [{allow,"127.0.0.0/8"},
 2575:                                               {deny,"0.0.0.0"}]}
 2576:                                 ]),
 2577:           ip_access_register(<<"0.0.0.0">>)),
 2578:     ?cfgh(modopts(mod_register, [{access,register},
 2579:                                  {ip_access, [{allow,"127.0.0.0/8"},
 2580:                                               {deny,"0.0.0.4"}]}
 2581:                                 ]),
 2582:           ip_access_register(<<"0.0.0.4">>)),
 2583:     ?cfgh(modopts(mod_register, [{access,register},
 2584:                                  {ip_access, [{allow,"127.0.0.0/8"},
 2585:                                               {deny,"::1"}]}
 2586:                                 ]),
 2587:           ip_access_register(<<"::1">>)),
 2588:     ?cfgh(modopts(mod_register, [{access,register},
 2589:                                  {ip_access, [{allow,"127.0.0.0/8"},
 2590:                                               {deny,"::1/128"}]}
 2591:                                 ]),
 2592:           ip_access_register(<<"::1/128">>)),
 2593:     ?errh(invalid_ip_access_register()),
 2594:     ?errh(invalid_ip_access_register_ipv6()),
 2595:     ?errh(ip_access_register(<<"hello">>)),
 2596:     ?errh(ip_access_register(<<"0.d">>)),
 2597:     ?cfgh(modopts(mod_register, [{welcome_message, {"Subject", "Body"}}]),
 2598:           welcome_message()),
 2599:     %% List of jids
 2600:     ?cfgh(modopts(mod_register, [{registration_watchers,
 2601:                                   [<<"alice@bob">>, <<"ilovemongoose@help">>]}]),
 2602:           registration_watchers([<<"alice@bob">>, <<"ilovemongoose@help">>])),
 2603:     ?errh(registration_watchers([<<"alice@bob">>, <<"jids@have@no@feelings!">>])),
 2604:     %% non-negative integer
 2605:     ?cfgh(modopts(mod_register, [{password_strength, 42}]),
 2606:           password_strength_register(42)),
 2607:     ?errh(password_strength_register(<<"42">>)),
 2608:     ?errh(password_strength_register(<<"strong">>)),
 2609:     ?errh(password_strength_register(-150)),
 2610:     ?errh(welcome_message(<<"Subject">>, 1)),
 2611:     ?errh(welcome_message(1, <<"Body">>)),
 2612:     check_iqdisc(mod_register).
 2613: 
 2614: welcome_message() ->
 2615:     welcome_message(<<"Subject">>, <<"Body">>).
 2616: 
 2617: welcome_message(S, B) ->
 2618:     Opts = #{<<"welcome_message">> => #{<<"subject">> => S, <<"body">> => B}},
 2619:     #{<<"modules">> => #{<<"mod_register">> => Opts}}.
 2620: 
 2621: password_strength_register(Strength) ->
 2622:     Opts = #{<<"password_strength">> => Strength},
 2623:     #{<<"modules">> => #{<<"mod_register">> => Opts}}.
 2624: 
 2625: ip_access_register(Ip) ->
 2626:     Opts = #{<<"access">> => <<"register">>,
 2627:              <<"ip_access">> =>
 2628:                  [#{<<"address">> => <<"127.0.0.0/8">>, <<"policy">> => <<"allow">>},
 2629:                   #{<<"address">> => Ip, <<"policy">> => <<"deny">>}]},
 2630:     #{<<"modules">> => #{<<"mod_register">> => Opts}}.
 2631: 
 2632: invalid_ip_access_register() ->
 2633:     Opts = #{<<"access">> => <<"register">>,
 2634:              <<"ip_access">> =>
 2635:                  [#{<<"address">> => <<"127.0.0.0/8">>, <<"policy">> => <<"allawww">>},
 2636:                   #{<<"address">> => <<"8.8.8.8">>, <<"policy">> => <<"denyh">>}]},
 2637:     #{<<"modules">> => #{<<"mod_register">> => Opts}}.
 2638: 
 2639: invalid_ip_access_register_ipv6() ->
 2640:     Opts = #{<<"access">> => <<"register">>,
 2641:              <<"ip_access">> =>
 2642:                  [#{<<"address">> => <<"::1/129">>, <<"policy">> => <<"allow">>}]},
 2643:     #{<<"modules">> => #{<<"mod_register">> => Opts}}.
 2644: 
 2645: registration_watchers(JidBins) ->
 2646:     Opts = #{<<"registration_watchers">> => JidBins},
 2647:     #{<<"modules">> => #{<<"mod_register">> => Opts}}.
 2648: 
 2649: mod_roster(_Config) ->
 2650:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_roster">> => Opts}} end,
 2651:     M = fun(Cfg) -> modopts(mod_roster, Cfg) end,
 2652: 
 2653:     ?cfgh(M([{versioning, false}]),
 2654:           T(#{<<"versioning">> => false})),
 2655:     ?cfgh(M([{store_current_id, false}]),
 2656:           T(#{<<"store_current_id">> => false})),
 2657:     ?cfgh(M([{backend, mnesia}]),
 2658:           T(#{<<"backend">> => <<"mnesia">>})),
 2659:     ?cfgh(M([{bucket_type, <<"rosters">>}]),
 2660:           T(#{<<"riak">> => #{<<"bucket_type">> => <<"rosters">>}})),
 2661:     ?cfgh(M([{version_bucket_type, <<"roster_versions">>}]),
 2662:           T(#{<<"riak">> => #{<<"version_bucket_type">> => <<"roster_versions">>}})),
 2663: 
 2664:     ?errh(T(#{<<"versioning">> => 1})),
 2665:     ?errh(T(#{<<"store_current_id">> => 1})),
 2666:     ?errh(T(#{<<"backend">> => 1})),
 2667:     ?errh(T(#{<<"backend">> => <<"iloveyou">>})),
 2668:     ?errh(T(#{<<"riak">> => #{<<"version_bucket_type">> => 1}})),
 2669:     ?errh(T(#{<<"riak">> => #{<<"bucket_type">> => 1}})),
 2670:     check_iqdisc(mod_roster).
 2671: 
 2672: mod_shared_roster_ldap(_Config) ->
 2673:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_shared_roster_ldap">> => Opts}} end,
 2674:     M = fun(Cfg) -> modopts(mod_shared_roster_ldap, Cfg) end,
 2675:     ?cfgh(M([{ldap_pool_tag, default}]),
 2676:           T(#{<<"ldap_pool_tag">> => <<"default">>})),
 2677:     ?cfgh(M([{ldap_base,  "string"}]),
 2678:           T(#{<<"ldap_base">> => <<"string">>})),
 2679:     ?cfgh(M([{ldap_deref, never}]),
 2680:           T(#{<<"ldap_deref">> => <<"never">>})),
 2681:     %% Options: attributes
 2682:     ?cfgh(M([ {ldap_groupattr, "cn"}]),
 2683:           T(#{<<"ldap_groupattr">> => <<"cn">>})),
 2684:     ?cfgh(M([{ldap_groupdesc, "default"}]),
 2685:           T(#{<<"ldap_groupdesc">> => <<"default">>})),
 2686:     ?cfgh(M([{ldap_userdesc, "cn"}]),
 2687:           T(#{<<"ldap_userdesc">> => <<"cn">>})),
 2688:     ?cfgh(M([{ldap_useruid, "cn"}]),
 2689:           T(#{<<"ldap_useruid">> => <<"cn">>})),
 2690:     ?cfgh(M([{ldap_memberattr, "memberUid"}]),
 2691:           T(#{<<"ldap_memberattr">> => <<"memberUid">>})),
 2692:     ?cfgh(M([{ldap_memberattr_format, "%u"}]),
 2693:           T(#{<<"ldap_memberattr_format">> => <<"%u">>})),
 2694:     ?cfgh(M([{ldap_memberattr_format_re,""}]),
 2695:           T(#{<<"ldap_memberattr_format_re">> => <<"">>})),
 2696:     %% Options: parameters
 2697:     ?cfgh(M([ {ldap_auth_check, true}]),
 2698:           T(#{<<"ldap_auth_check">> => true})),
 2699:     ?cfgh(M([{ldap_user_cache_validity, 300}]),
 2700:           T(#{<<"ldap_user_cache_validity">> => 300})),
 2701:     ?cfgh(M([{ldap_group_cache_validity, 300}]),
 2702:           T(#{<<"ldap_group_cache_validity">> => 300})),
 2703:     ?cfgh(M([{ldap_user_cache_size, 300}]),
 2704:           T(#{<<"ldap_user_cache_size">> => 300})),
 2705:     ?cfgh(M([{ldap_group_cache_size, 300}]),
 2706:           T(#{<<"ldap_group_cache_size">> => 300})),
 2707:     %% Options: LDAP filters
 2708:     ?cfgh(M([{ldap_rfilter, "rfilter_test"}]),
 2709:           T(#{<<"ldap_rfilter">> => <<"rfilter_test">>})),
 2710:     ?cfgh(M([{ldap_gfilter, "gfilter_test"}]),
 2711:           T(#{<<"ldap_gfilter">> => <<"gfilter_test">>})),
 2712:     ?cfgh(M([{ldap_ufilter, "ufilter_test"}]),
 2713:           T(#{<<"ldap_ufilter">> => <<"ufilter_test">>})),
 2714:     ?cfgh(M([{ldap_filter, "filter_test"}]),
 2715:           T(#{<<"ldap_filter">> => <<"filter_test">>})),
 2716:     ?errh(T(#{<<"ldap_pool_tag">> => 1})),
 2717:     ?errh(T(#{<<"ldap_base">> => 1})),
 2718:     ?errh(T(#{<<"ldap_deref">> => 1})),
 2719:     %% Options: attributes
 2720:     ?errh(T(#{<<"ldap_groupattr">> => 1})),
 2721:     ?errh(T(#{<<"ldap_groupdesc">> => 1})),
 2722:     ?errh(T(#{<<"ldap_userdesc">> => 1})),
 2723:     ?errh(T(#{<<"ldap_useruid">> => 1})),
 2724:     ?errh(T(#{<<"ldap_memberattr">> => 1})),
 2725:     ?errh(T(#{<<"ldap_memberattr_format">> => 1})),
 2726:     ?errh(T(#{<<"ldap_memberattr_format_re">> => 1})),
 2727:     %% Options: parameters
 2728:     ?errh(T(#{<<"ldap_auth_check">> => 1})),
 2729:     ?errh(T(#{<<"ldap_user_cache_validity">> => -1})),
 2730:     ?errh(T(#{<<"ldap_group_cache_validity">> => -1})),
 2731:     ?errh(T(#{<<"ldap_user_cache_size">> => -1})),
 2732:     ?errh(T(#{<<"ldap_group_cache_size">> => -1})),
 2733:     %% Options: LDAP filters
 2734:     ?errh(T(#{<<"ldap_rfilter">> => 1})),
 2735:     ?errh(T(#{<<"ldap_gfilter">> => 1})),
 2736:     ?errh(T(#{<<"ldap_ufilter">> => 1})),
 2737:     ?errh(T(#{<<"ldap_filter">> => 1})).
 2738: 
 2739: mod_sic(_Config) ->
 2740:     check_iqdisc(mod_sic),
 2741:     ?cfgh(modopts(mod_sic, []), #{<<"modules">> => #{<<"mod_sic">> => #{}}}).
 2742: 
 2743: mod_stream_management(_Config) ->
 2744:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_stream_management">> => Opts}} end,
 2745:     M = fun(Cfg) -> modopts(mod_stream_management, Cfg) end,
 2746:     ?cfgh(M([{buffer_max, no_buffer}]),  T(#{<<"buffer">> => false})),
 2747:     ?cfgh(M([{buffer_max, 10}]),  T(#{<<"buffer_max">> => 10})),
 2748:     ?cfgh(M([{ack_freq, never}]), T(#{<<"ack">> => false})),
 2749:     ?cfgh(M([{ack_freq, 1}]), T(#{<<"ack_freq">> => 1})),
 2750:     ?cfgh(M([{resume_timeout, 600}]), T(#{<<"resume_timeout">> => 600})),
 2751: 
 2752:     ?errh(T(#{<<"buffer">> => 0})),
 2753:     ?errh(T(#{<<"buffer_max">> => -1})),
 2754:     ?errh(T(#{<<"ack">> => <<"false">>})),
 2755:     ?errh(T(#{<<"ack_freq">> => 0})),
 2756:     ?errh(T(#{<<"resume_timeout">> => true})).
 2757: 
 2758: mod_stream_management_stale_h(_Config) ->
 2759:     T = fun(Opts) -> #{<<"modules">> =>
 2760:                            #{<<"mod_stream_management">> => #{<<"stale_h">> => Opts}}} end,
 2761:     M = fun(Cfg) -> modopts(mod_stream_management, [{stale_h, Cfg}]) end,
 2762:     ?cfgh(M([{enabled, true}]), T(#{<<"enabled">> => true})),
 2763:     ?cfgh(M([{stale_h_repeat_after, 1800}]), T(#{<<"repeat_after">> => 1800})),
 2764:     ?cfgh(M([{stale_h_geriatric, 3600}]), T(#{<<"geriatric">> => 3600})),
 2765: 
 2766:     ?errh(T(#{<<"enabled">> => <<"true">>})),
 2767:     ?errh(T(#{<<"repeat_after">> => -1})),
 2768:     ?errh(T(#{<<"geriatric">> => <<"one">>})).
 2769: 
 2770: mod_time(_Config) ->
 2771:     check_iqdisc(mod_time),
 2772:     ?cfgh(modopts(mod_time, []), #{<<"modules">> => #{<<"mod_time">> => #{}}}).
 2773: 
 2774: mod_vcard(_Config) ->
 2775:     check_iqdisc(mod_vcard),
 2776:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_vcard">> => Opts}} end,
 2777:     M = fun(Cfg) -> modopts(mod_vcard, Cfg) end,
 2778:     ?cfgh(M([{iqdisc, one_queue}]),
 2779:           T(#{<<"iqdisc">> => #{<<"type">> => <<"one_queue">>}})),
 2780:     ?cfgh(M([{host, {prefix, <<"vjud.">>}}]),
 2781:           T(#{<<"host">> => <<"vjud.@HOST@">>})),
 2782:     ?cfgh(M([{host, {fqdn, <<"vjud.test">>}}]),
 2783:           T(#{<<"host">> => <<"vjud.test">>})),
 2784:     ?cfgh(M([{search, true}]),
 2785:           T(#{<<"search">> => true})),
 2786:     ?cfgh(M([{backend, mnesia}]),
 2787:           T(#{<<"backend">> => <<"mnesia">>})),
 2788:     ?cfgh(M([{matches, infinity}]),
 2789:           T(#{<<"matches">> => <<"infinity">>})),
 2790:     %% ldap
 2791:     ?cfgh(M([{ldap_pool_tag, default}]),
 2792:           T(#{<<"ldap_pool_tag">> => <<"default">>})),
 2793:     ?cfgh(M([{ldap_base, "ou=Users,dc=ejd,dc=com"}]),
 2794:           T(#{<<"ldap_base">> => <<"ou=Users,dc=ejd,dc=com">>})),
 2795:     ?cfgh(M([{ldap_filter, <<"(&(objectClass=shadowAccount)(memberOf=Jabber Users))">>}]),
 2796:           T(#{<<"ldap_filter">> => <<"(&(objectClass=shadowAccount)(memberOf=Jabber Users))">>})),
 2797:     ?cfgh(M([{ldap_deref, never}]),
 2798:           T(#{<<"ldap_deref">> => <<"never">>})),
 2799:     ?cfgh(M([{ldap_search_operator, 'or'}]),
 2800:           T(#{<<"ldap_search_operator">> => <<"or">>})),
 2801:     ?cfgh(M([{ldap_binary_search_fields, [<<"PHOTO">>]}]),
 2802:           T(#{<<"ldap_binary_search_fields">> => [<<"PHOTO">>]})),
 2803:     %% riak
 2804:     ?cfgh(M([{bucket_type, <<"vcard">>}]),
 2805:           T(#{<<"riak">> =>  #{<<"bucket_type">> => <<"vcard">>}})),
 2806:     ?cfgh(M([{search_index, <<"vcard">>}]),
 2807:           T(#{<<"riak">> =>  #{<<"search_index">> => <<"vcard">>}})),
 2808: 
 2809:     ?errh(T(#{<<"host">> => 1})),
 2810:     ?errh(T(#{<<"host">> => <<"is this a host? no.">>})),
 2811:     ?errh(T(#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
 2812:     ?errh(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
 2813:     ?errh(T(#{<<"search">> => 1})),
 2814:     ?errh(T(#{<<"backend">> => <<"mememesia">>})),
 2815:     ?errh(T(#{<<"matches">> => -1})),
 2816:     %% ldap
 2817:     ?errh(T(#{<<"ldap_pool_tag">> => -1})),
 2818:     ?errh(T(#{<<"ldap_base">> => -1})),
 2819:     ?errh(T(#{<<"ldap_field">> => -1})),
 2820:     ?errh(T(#{<<"ldap_deref">> => <<"nevernever">>})),
 2821:     ?errh(T(#{<<"ldap_search_operator">> => <<"more">>})),
 2822:     ?errh(T(#{<<"ldap_binary_search_fields">> => [1]})),
 2823:     %% riak
 2824:     ?errh(T(#{<<"riak">> =>  #{<<"bucket_type">> => 1}})),
 2825:     ?errh(T(#{<<"riak">> =>  #{<<"search_index">> => 1}})).
 2826: 
 2827: mod_vcard_ldap_uids(_Config) ->
 2828:     T = fun(Opts) -> #{<<"modules">> =>
 2829:                            #{<<"mod_vcard">> => #{<<"ldap_uids">> => Opts}}} end,
 2830:     M = fun(Cfg) -> modopts(mod_vcard, [{ldap_uids, Cfg}]) end,
 2831:     RequiredOpts = #{<<"attr">> => <<"name">>},
 2832:     ExpectedCfg = <<"name">>,
 2833:     ?cfgh(M([]), T([])),
 2834:     ?cfgh(M([ExpectedCfg]), T([RequiredOpts])),
 2835:     ?cfgh(M([{<<"name">>, <<"%u@mail.example.org">>}]),
 2836:           T([RequiredOpts#{<<"format">> => <<"%u@mail.example.org">>}])),
 2837:     ?cfgh(M([{<<"name">>, <<"%u@mail.example.org">>}, ExpectedCfg]),
 2838:           T([RequiredOpts#{<<"format">> => <<"%u@mail.example.org">>}, RequiredOpts])),
 2839:     [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)],
 2840:     ?errh(T(RequiredOpts#{<<"attr">> := 1})),
 2841:     ?errh(T(RequiredOpts#{<<"format">> => true})).
 2842: 
 2843: mod_vcard_ldap_vcard_map(_Config) ->
 2844:     T = fun(Opts) -> #{<<"modules">> =>
 2845:                            #{<<"mod_vcard">> => #{<<"ldap_vcard_map">> => Opts}}} end,
 2846:     M = fun(Cfg) -> modopts(mod_vcard, [{ldap_vcard_map, Cfg}]) end,
 2847:     RequiredOpts = #{<<"vcard_field">> => <<"FAMILY">>,
 2848:                      <<"ldap_pattern">> => <<"%s">>,
 2849:                      <<"ldap_field">> => <<"sn">>},
 2850:     ExpectedCfg = {<<"FAMILY">>, <<"%s">>, [<<"sn">>]},
 2851:     ?cfgh(M([]), T([])),
 2852:     ?cfgh(M([ExpectedCfg]), T([RequiredOpts])),
 2853:     [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)],
 2854:     ?errh(T(RequiredOpts#{<<"vcard_field">> := false})),
 2855:     ?errh(T(RequiredOpts#{<<"ldap_pattern">> := false})),
 2856:     ?errh(T(RequiredOpts#{<<"ldap_field">> := -1})).
 2857: 
 2858: mod_vcard_ldap_search_fields(_Config) ->
 2859:     T = fun(Opts) -> #{<<"modules">> =>
 2860:                            #{<<"mod_vcard">> => #{<<"ldap_search_fields">> => Opts}}} end,
 2861:     M = fun(Cfg) -> modopts(mod_vcard, [{ldap_search_fields, Cfg}]) end,
 2862:     RequiredOpts = #{<<"search_field">> => <<"Full Name">>,
 2863:                      <<"ldap_field">> => <<"cn">>},
 2864:     ExpectedCfg = {<<"Full Name">>, <<"cn">>},
 2865:     ?cfgh(M([]), T([])),
 2866:     ?cfgh(M([ExpectedCfg]), T([RequiredOpts])),
 2867:     [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)],
 2868:     ?errh(T(RequiredOpts#{<<"search_field">> := false})),
 2869:     ?errh(T(RequiredOpts#{<<"ldap_field">> := -1})).
 2870: 
 2871: mod_vcard_ldap_search_reported(_Config) ->
 2872:     T = fun(Opts) -> #{<<"modules">> =>
 2873:                            #{<<"mod_vcard">> => #{<<"ldap_search_reported">> => Opts}}} end,
 2874:     M = fun(Cfg) -> modopts(mod_vcard, [{ldap_search_reported, Cfg}]) end,
 2875:     RequiredOpts = #{<<"search_field">> => <<"Full Name">>,
 2876:                      <<"vcard_field">> => <<"FN">>},
 2877:     ExpectedCfg = {<<"Full Name">>, <<"FN">>},
 2878:     ?cfgh(M([]), T([])),
 2879:     ?cfgh(M([ExpectedCfg]), T([RequiredOpts])),
 2880:     [?errh(T([maps:remove(Key, RequiredOpts)])) || Key <- maps:keys(RequiredOpts)],
 2881:     ?errh(T(RequiredOpts#{<<"search_field">> := false})),
 2882:     ?errh(T(RequiredOpts#{<<"vcard_field">> := -1})).
 2883: 
 2884: mod_version(_Config) ->
 2885:     T = fun(Opts) -> #{<<"modules">> => #{<<"mod_version">> => Opts}} end,
 2886:     ?cfgh(modopts(mod_version, [{os_info, false}]), T(#{<<"os_info">> => false})),
 2887:     ?errh(T(#{<<"os_info">> => 1})),
 2888:     check_iqdisc(mod_version).
 2889: 
 2890: modules_without_config(_Config) ->
 2891:     ?cfgh(modopts(mod_amp, []), #{<<"modules">> => #{<<"mod_amp">> => #{}}}),
 2892:     ?errh(#{<<"modules">> => #{<<"mod_wrong">> => #{}}}).
 2893: 
 2894: %% Services
 2895: 
 2896: service_admin_extra(_Config) ->
 2897:     T = fun(Opts) -> #{<<"services">> => #{<<"service_admin_extra">> => Opts}} end,
 2898:     ?cfg(servopts(service_admin_extra, [{submods, [node]}]),
 2899:          T(#{<<"submods">> => [<<"node">>]})),
 2900:     ?err(T(#{<<"submods">> => 1})),
 2901:     ?err(T(#{<<"submods">> => [1]})),
 2902:     ?err(T(#{<<"submods">> => [<<"nodejshaha">>]})),
 2903:     ok.
 2904: 
 2905: service_mongoose_system_metrics(_Config) ->
 2906:     M = service_mongoose_system_metrics,
 2907:     T = fun(Opts) -> #{<<"services">> => #{<<"service_mongoose_system_metrics">> => Opts}} end,
 2908:     ?cfg(servopts(M, [{initial_report, 5000}]),
 2909:          T(#{<<"initial_report">> => 5000})),
 2910:     ?cfg(servopts(M, [{periodic_report, 5000}]),
 2911:          T(#{<<"periodic_report">> => 5000})),
 2912:     ?cfg(servopts(M, [{tracking_id, "UA-123456789"}]),
 2913:          T(#{<<"tracking_id">> => <<"UA-123456789">>})),
 2914:     ?cfg(servopts(M, [no_report]),
 2915:          T(#{<<"report">> => false})),
 2916:     %% error cases
 2917:     ?err(T(#{<<"initial_report">> => <<"forever">>})),
 2918:     ?err(T(#{<<"periodic_report">> => <<"forever">>})),
 2919:     ?err(T(#{<<"initial_report">> => -1})),
 2920:     ?err(T(#{<<"periodic_report">> => -1})),
 2921:     ?err(T(#{<<"tracking_id">> => 666})),
 2922:     ok.
 2923: 
 2924: %% Helpers for module tests
 2925: 
 2926: iqdisc({queues, Workers}) -> #{<<"type">> => <<"queues">>, <<"workers">> => Workers};
 2927: iqdisc(Atom) -> #{<<"type">> => atom_to_binary(Atom, utf8)}.
 2928: 
 2929: iq_disc_generic(Module, RequiredOpts, Value) ->
 2930:     Opts = RequiredOpts#{<<"iqdisc">> => Value},
 2931:     #{<<"modules">> => #{atom_to_binary(Module, utf8) => Opts}}.
 2932: 
 2933: check_iqdisc(Module) ->
 2934:     check_iqdisc(Module, [], #{}).
 2935: 
 2936: check_iqdisc(Module, ExpectedCfg, RequiredOpts) ->
 2937:     ?cfgh(modopts(Module, ExpectedCfg ++ [{iqdisc, {queues, 10}}]),
 2938:           iq_disc_generic(Module, RequiredOpts, iqdisc({queues, 10}))),
 2939:     ?cfgh(modopts(Module, ExpectedCfg ++ [{iqdisc, parallel}]),
 2940:           iq_disc_generic(Module, RequiredOpts, iqdisc(parallel))),
 2941:     ?errh(iq_disc_generic(Module, RequiredOpts, iqdisc(bad_haha))).
 2942: 
 2943: modopts(Mod, Opts) ->
 2944:     [{modules, [{Mod, Opts}]}].
 2945: 
 2946: servopts(Service, Opts) ->
 2947:     [{services, [{Service, Opts}]}].
 2948: 
 2949: %% helpers for 'listen' tests
 2950: 
 2951: listener_config(Mod, Opts) ->
 2952:     [{listen, [{{5222, {0, 0, 0, 0}, tcp}, Mod, Opts}]}].
 2953: 
 2954: http_handler_raw(Type, Opts) ->
 2955:     listen_raw(<<"http">>, #{<<"handlers">> =>
 2956:                                  #{Type =>
 2957:                                        [Opts#{<<"host">> => <<"localhost">>,
 2958:                                               <<"path">> => <<"/api">>}]
 2959:                                   }}).
 2960: 
 2961: listen_raw(Type, Opts) ->
 2962:     #{<<"listen">> => #{Type => [Opts#{<<"port">> => 5222}]}}.
 2963: 
 2964: %% helpers for 'auth' tests
 2965: 
 2966: auth_ldap_raw(Opts) ->
 2967:     auth_raw(<<"ldap">>, Opts).
 2968: 
 2969: auth_raw(Method, Opts) ->
 2970:     #{<<"auth">> => #{Method => Opts}}.
 2971: 
 2972: %% helpers for 'pool' tests
 2973: 
 2974: pool_config(Pool) ->
 2975:     [{outgoing_pools, [Pool]}].
 2976: 
 2977: pool_raw(Type, Tag, Opts) ->
 2978:     #{<<"outgoing_pools">> => #{Type => #{Tag => Opts}}}.
 2979: 
 2980: pool_conn_raw(Type, Opts) ->
 2981:     #{<<"outgoing_pools">> => #{Type => #{<<"default">> => #{<<"connection">> => Opts}}}}.
 2982: 
 2983: rdbms_opts() ->
 2984:     #{<<"driver">> => <<"pgsql">>,
 2985:       <<"host">> => <<"localhost">>,
 2986:       <<"database">> => <<"db">>,
 2987:       <<"username">> => <<"dbuser">>,
 2988:       <<"password">> => <<"secret">>}.
 2989: 
 2990: %% helpers for 'host_config' tests
 2991: 
 2992: -spec assert_option_host_or_global(key_prefix(), mongoose_config:value(),
 2993:                                    mongoose_config_parser_toml:toml_section()) -> any().
 2994: assert_option_host_or_global(KeyPrefix, Value, RawConfig) ->
 2995:     assert_options_host_or_global([{KeyPrefix, Value}], RawConfig).
 2996: 
 2997: -spec assert_options_host_or_global([{key_prefix(), mongoose_config:value()}],
 2998:                                     mongoose_config_parser_toml:toml_section()) -> any().
 2999: assert_options_host_or_global(ExpectedOptions, RawConfig) ->
 3000:     assert_options_host(ExpectedOptions, RawConfig),
 3001:     assert_options_global(ExpectedOptions, RawConfig).
 3002: 
 3003: -spec assert_options_global([{key_prefix(), mongoose_config:value()}],
 3004:                             mongoose_config_parser_toml:toml_section()) -> any().
 3005: assert_options_global(ExpectedOptions, RawConfig) ->
 3006:     GlobalConfig = parse(RawConfig),
 3007:     GlobalOptions = [{host_key(Key, global), Value} || {Key, Value} <- ExpectedOptions],
 3008:     assert_options(GlobalOptions, GlobalConfig).
 3009: 
 3010: -spec assert_options_host([{key_prefix(), mongoose_config:value()}],
 3011:                           mongoose_config_parser_toml:toml_section()) -> any().
 3012: assert_options_host(ExpectedOptions, RawConfig) ->
 3013:     HostConfig = parse(host_config(RawConfig)),
 3014:     HostOptions = [{host_key(Key, ?HOST_TYPE), Value} || {Key, Value} <- ExpectedOptions],
 3015:     assert_options(HostOptions, HostConfig).
 3016: 
 3017: -type key_prefix() :: {shaper | acl | access, atom()} | atom() | {atom(), jid:lserver()}.
 3018: 
 3019: %% @doc Create full per-host config key for host-or-global options
 3020: -spec host_key(key_prefix(), mongooseim:host_type_or_global()) -> mongoose_config:key().
 3021: host_key({Key, Tag}, HostType) when Key =:= shaper;
 3022:                                     Key =:= acl;
 3023:                                     Key =:= access ->
 3024:     {Key, Tag, HostType};
 3025: host_key(Key, HostType) ->
 3026:     {Key, HostType}.
 3027: 
 3028: -spec assert_error_host_or_global(mongoose_config_parser_toml:toml_section()) -> any().
 3029: assert_error_host_or_global(RawConfig) ->
 3030:     assert_error(parse(RawConfig)),
 3031:     assert_error(parse(host_config(RawConfig))).
 3032: 
 3033: host_config(Config) ->
 3034:     #{<<"host_config">> => [Config#{<<"host_type">> => ?HOST_TYPE}]}.
 3035: 
 3036: -spec parse(map()) -> [mongoose_config_parser_toml:config()].
 3037: parse(M0) ->
 3038:     %% As 'hosts' (or 'host_types') and 'default_server_domain' options are mandatory,
 3039:     %% this function inserts them with dummy values if they are missing.
 3040:     %% To prevent the insertion, add a 'without' option to the map, e.g. without => [<<"hosts">>]
 3041:     %% The resulting map is then passed to the TOML config parser.
 3042:     DummyDomainName = <<"dummy.domain.name">>,
 3043:     M = maybe_insert_dummy_domain(M0, DummyDomainName),
 3044:     mongoose_config_parser_toml:parse(M).
 3045: 
 3046: maybe_insert_dummy_domain(M, DomainName) ->
 3047:     DummyGenM = #{<<"default_server_domain">> => DomainName,
 3048:                   <<"hosts">> => [DomainName]},
 3049:     {FilteredGenM, RawConfig} = case maps:take(without, M) of
 3050:                                     {Keys, Cfg} -> {maps:without(Keys, DummyGenM), Cfg};
 3051:                                     error -> {DummyGenM, M}
 3052:                                 end,
 3053:     OldGenM = maps:get(<<"general">>, RawConfig, #{}),
 3054:     NewGenM = maps:merge(FilteredGenM, OldGenM),
 3055:     RawConfig#{<<"general">> => NewGenM}.
 3056: 
 3057: %% helpers for testing individual options
 3058: 
 3059: -spec assert_options([{mongoose_config:key(), mongoose_config:value()}],
 3060:                      [mongoose_config_parser_toml:config()]) -> any().
 3061: assert_options(ExpectedOptions, Config) ->
 3062:     lists:foreach(fun({Key, Value}) -> assert_option(Key, Value, Config) end, ExpectedOptions).
 3063: 
 3064: -spec assert_option(mongoose_config:key(), mongoose_config:value(),
 3065:                     [mongoose_config_parser_toml:config()]) -> any().
 3066: assert_option(Key, Value, Config) ->
 3067:     case lists:keyfind(Key, 1, Config) of
 3068:         false -> ct:fail({"option not found", Key, Value, Config});
 3069:         ActualOpt -> handle_config_option({Key, Value}, ActualOpt)
 3070:     end.
 3071: 
 3072: -spec assert_error([mongoose_config_parser_toml:config()]) -> any().
 3073: assert_error(Config) ->
 3074:     ?assertMatch([#{class := error, what := _}|_],
 3075:                  mongoose_config_parser_toml:extract_errors(Config)).
 3076: 
 3077: %% helpers for file tests
 3078: 
 3079: test_config_file(Config, File) ->
 3080:     OptionsPath = ejabberd_helper:data(Config, File ++ ".options"),
 3081:     {ok, ExpectedOpts} = file:consult(OptionsPath),
 3082: 
 3083:     TOMLPath = ejabberd_helper:data(Config, File ++ ".toml"),
 3084:     State = mongoose_config_parser:parse_file(TOMLPath),
 3085:     TOMLOpts = mongoose_config_parser:state_to_opts(State),
 3086: 
 3087:     %% Save the parsed TOML options
 3088:     %% - for debugging
 3089:     %% - to update tests after a config change - always check the diff!
 3090:     save_opts(OptionsPath ++ ".parsed", TOMLOpts),
 3091:     compare_config(ExpectedOpts, TOMLOpts).
 3092: 
 3093: save_opts(Path, Opts) ->
 3094:     FormattedOpts = [io_lib:format("~p.~n", [Opt]) || Opt <- lists:sort(Opts)],
 3095:     file:write_file(Path, FormattedOpts).
 3096: 
 3097: compare_config(C1, C2) ->
 3098:     compare_unordered_lists(C1, C2, fun handle_config_option/2).
 3099: 
 3100: handle_config_option({K1, V1}, {K2, V2}) ->
 3101:     ?eq(K1, K2),
 3102:     compare_values(K1, V1, V2);
 3103: handle_config_option(Opt1, Opt2) ->
 3104:     ?eq(Opt1, Opt2).
 3105: 
 3106: compare_values(listen, V1, V2) ->
 3107:     compare_unordered_lists(V1, V2, fun handle_listener/2);
 3108: compare_values({auth, _}, V1, V2) ->
 3109:     compare_maps(V1, V2);
 3110: compare_values(outgoing_pools, V1, V2) ->
 3111:     compare_unordered_lists(V1, V2, fun handle_conn_pool/2);
 3112: compare_values({modules, _}, V1, V2) ->
 3113:     compare_unordered_lists(V1, V2, fun handle_module/2);
 3114: compare_values(services, V1, V2) ->
 3115:     compare_unordered_lists(V1, V2, fun handle_item_with_opts/2);
 3116: compare_values({auth_method, _}, V1, V2) when is_atom(V1) ->
 3117:     ?eq([V1], V2);
 3118: compare_values({s2s_addr, _}, {_, _, _, _} = IP1, IP2) ->
 3119:     ?eq(inet:ntoa(IP1), IP2);
 3120: compare_values(s2s_dns_options, V1, V2) ->
 3121:     compare_unordered_lists(V1, V2);
 3122: compare_values(K, V1, V2) ->
 3123:     ?eq({K, V1}, {K, V2}).
 3124: 
 3125: handle_listener({P1, M1, O1}, {P2, M2, O2}) ->
 3126:     ?eq(P1, P2),
 3127:     ?eq(M1, M2),
 3128:     compare_unordered_lists(O1, O2, fun handle_listener_option/2).
 3129: 
 3130: handle_listener_option({modules, M1}, {modules, M2}) ->
 3131:     compare_unordered_lists(M1, M2, fun handle_listener_module/2);
 3132: handle_listener_option({transport_options, O1}, {transport_options, O2}) ->
 3133:     compare_unordered_lists(O1, O2);
 3134: handle_listener_option(V1, V2) -> ?eq(V1, V2).
 3135: 
 3136: handle_listener_module({H1, P1, M1}, M2) ->
 3137:     handle_listener_module({H1, P1, M1, []}, M2);
 3138: handle_listener_module({H1, P1, M1, O1}, {H2, P2, M2, O2}) ->
 3139:     ?eq(H1, H2),
 3140:     ?eq(P1, P2),
 3141:     ?eq(M1, M2),
 3142:     compare_listener_module_options(M1, O1, O2).
 3143: 
 3144: compare_listener_module_options(mod_websockets, L1, L2) ->
 3145:     E1 = proplists:get_value(ejabberd_service, L1, []),
 3146:     E2 = proplists:get_value(ejabberd_service, L2, []),
 3147:     T1 = proplists:delete(ejabberd_service, L1),
 3148:     T2 = proplists:delete(ejabberd_service, L2),
 3149:     compare_unordered_lists(E1, E2),
 3150:     compare_unordered_lists(T1, T2);
 3151: compare_listener_module_options(_, O1, O2) ->
 3152:     ?eq(O1, O2).
 3153: 
 3154: handle_item_with_opts({M1, O1}, {M2, O2}) ->
 3155:     ?eq(M1, M2),
 3156:     compare_unordered_lists(O1, O2).
 3157: 
 3158: handle_conn_pool({Type1, Scope1, Tag1, POpts1, COpts1},
 3159:                  {Type2, Scope2, Tag2, POpts2, COpts2}) ->
 3160:     ?eq(Type1, Type2),
 3161:     ?eq(Scope1, Scope2),
 3162:     ?eq(Tag1, Tag2),
 3163:     compare_unordered_lists(POpts1, POpts2),
 3164:     compare_unordered_lists(COpts1, COpts2, fun handle_conn_opt/2).
 3165: 
 3166: handle_conn_opt({server, {D1, H1, DB1, U1, P1, O1}},
 3167:                 {server, {D2, H2, DB2, U2, P2, O2}}) ->
 3168:     ?eq(D1, D2),
 3169:     ?eq(H1, H2),
 3170:     ?eq(DB1, DB2),
 3171:     ?eq(U1, U2),
 3172:     ?eq(P1, P2),
 3173:     compare_unordered_lists(O1, O2, fun handle_db_server_opt/2);
 3174: handle_conn_opt(V1, V2) -> ?eq(V1, V2).
 3175: 
 3176: handle_db_server_opt({ssl_opts, O1}, {ssl_opts, O2}) ->
 3177:     compare_unordered_lists(O1, O2);
 3178: handle_db_server_opt(V1, V2) -> ?eq(V1, V2).
 3179: 
 3180: handle_module({mod_extdisco, Opts}, {mod_extdisco, Opts2}) ->
 3181:     compare_ordered_lists(Opts, Opts2, fun compare_unordered_lists/2);
 3182: handle_module({Name, Opts}, {Name2, Opts2}) ->
 3183:     ?eq(Name, Name2),
 3184:     compare_unordered_lists(Opts, Opts2, fun handle_module_options/2).
 3185: 
 3186: handle_module_options({configs, [Configs1]}, {configs, [Configs2]}) ->
 3187:     compare_unordered_lists(Configs1, Configs2, fun handle_module_options/2);
 3188: handle_module_options({Name, Opts}, {Name2, Opts2}) ->
 3189:     ?eq(Name, Name2),
 3190:     compare_unordered_lists(Opts, Opts2, fun handle_module_options/2);
 3191: handle_module_options(V1, V2) ->
 3192:     ?eq(V1, V2).
 3193: 
 3194: %% Generic assertions, use the 'F' handler for any custom cases
 3195: compare_unordered_lists(L1, L2) ->
 3196:     compare_unordered_lists(L1, L2, fun(V1, V2) -> ?eq(V1, V2) end).
 3197: 
 3198: compare_unordered_lists(L1, L2, F) ->
 3199:     SL1 = lists:sort(L1),
 3200:     SL2 = lists:sort(L2),
 3201:     compare_ordered_lists(SL1, SL2, F).
 3202: 
 3203: compare_ordered_lists([H1|T1], [H1|T2], F) ->
 3204:     compare_ordered_lists(T1, T2, F);
 3205: compare_ordered_lists([H1|T1] = L1, [H2|T2] = L2, F) ->
 3206:     try F(H1, H2)
 3207:     catch C:R:S ->
 3208:             ct:fail({"Failed to compare ordered lists", L1, L2, {C, R, S}})
 3209:     end,
 3210:     compare_ordered_lists(T1, T2, F);
 3211: compare_ordered_lists([], [], _) ->
 3212:     ok.
 3213: 
 3214: compare_maps(M1, M2) ->
 3215:     ?eq(M1, M2).
 3216: 
 3217: compare_maps(M1, M2, F) ->
 3218:     compare_unordered_lists(maps:to_list(M1), maps:to_list(M2), F).
 3219: 
 3220: create_files(Config) ->
 3221:     %% The files must exist for validation to pass
 3222:     Root = small_path_helper:repo_dir(Config),
 3223:     file:make_dir("priv"),
 3224:     PrivkeyPath = filename:join(Root, "tools/ssl/mongooseim/privkey.pem"),
 3225:     CertPath = filename:join(Root, "tools/ssl/mongooseim/cert.pem"),
 3226:     CaPath = filename:join(Root, "tools/ssl/ca/cacert.pem"),
 3227:     ok = file:write_file("priv/access_psk", ""),
 3228:     ok = file:write_file("priv/provision_psk", ""),
 3229:     ok = filelib:ensure_dir("www/muc/dummy"),
 3230:     ensure_copied(CaPath, "priv/ca.pem"),
 3231:     ensure_copied(CertPath, "priv/cert.pem"),
 3232:     ensure_copied(PrivkeyPath, "priv/dc1.pem").
 3233: 
 3234: ensure_copied(From, To) ->
 3235:     case file:copy(From, To) of
 3236:         {ok,_} ->
 3237:             ok;
 3238:         Other ->
 3239:             error(#{what => ensure_copied_failed, from => From, to => To,
 3240:                     reason => Other})
 3241:     end.