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