1: -module(graphql_domain_SUITE).
    2: 
    3:  -include_lib("eunit/include/eunit.hrl").
    4: 
    5:  -compile([export_all, nowarn_export_all]).
    6: 
    7: -import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]).
    8: -import(graphql_helper, [execute_command/4, get_ok_value/2, get_err_msg/1, skip_null_fields/1,
    9:                          execute_domain_admin_command/4, get_unauthorized/1, get_coercion_err_msg/1]).
   10: 
   11: -define(HOST_TYPE, <<"dummy auth">>).
   12: -define(SECOND_HOST_TYPE, <<"test type">>).
   13: -define(EXAMPLE_DOMAIN, <<"example.com">>).
   14: -define(SECOND_EXAMPLE_DOMAIN, <<"second.example.com">>).
   15: -define(DOMAIN_ADMIN_EXAMPLE_DOMAIN, <<"domain-admin.example.com">>).
   16: 
   17: suite() ->
   18:     require_rpc_nodes([mim]) ++ escalus:suite().
   19: 
   20: all() ->
   21:      [{group, domain_http},
   22:       {group, domain_cli},
   23:       {group, domain_admin_tests}].
   24: 
   25: groups() ->
   26:      [{domain_http, [sequence], domain_tests()},
   27:       {domain_cli, [sequence], domain_tests()},
   28:       {domain_admin_tests, [sequence], domain_admin_tests()}].
   29: 
   30: domain_tests() ->
   31:     [create_domain,
   32:      unknown_host_type_error_formatting,
   33:      add_static_domain_error_formatting,
   34:      remove_static_domain_error_formatting,
   35:      enable_static_domain_error_formatting,
   36:      disable_static_domain_error_formatting,
   37:      domain_duplicate_error_formatting,
   38:      domain_not_found_error_formatting_after_mutation_disable_domain,
   39:      domain_not_found_error_formatting_after_mutation_enable_domain,
   40:      domain_not_found_error_formatting_after_query,
   41:      wrong_host_type_error_formatting,
   42:      invalid_domain_name_error,
   43:      disable_domain,
   44:      enable_domain,
   45:      get_domains_by_host_type,
   46:      get_domain_details,
   47:      delete_domain,
   48:      request_delete_domain,
   49:      get_domains_after_deletion,
   50:      set_domain_password,
   51:      set_nonexistent_domain_password,
   52:      delete_domain_password,
   53:      delete_nonexistent_domain_password
   54:     ].
   55: 
   56: domain_admin_tests() ->
   57:     [domain_admin_get_domain_details,
   58:      domain_admin_set_domain_password,
   59:      domain_admin_create_domain_no_permission,
   60:      domain_admin_disable_domain_no_permission,
   61:      domain_admin_enable_domain_no_permission,
   62:      domain_admin_get_domains_by_host_type_no_permission,
   63:      domain_admin_get_domain_details_no_permission,
   64:      domain_admin_delete_domain_no_permission,
   65:      domain_admin_set_domain_password_no_permission,
   66:      domain_admin_delete_domain_password_no_permission
   67:     ].
   68: 
   69: init_per_suite(Config) ->
   70:     case mongoose_helper:is_rdbms_enabled(?HOST_TYPE) of
   71:         true ->
   72:             Config1 = ejabberd_node_utils:init(mim(), Config),
   73:             escalus:init_per_suite(Config1);
   74:         false ->
   75:             {skip, require_rdbms}
   76:     end.
   77: 
   78: end_per_suite(Config) ->
   79:     escalus_fresh:clean(),
   80:     escalus:end_per_suite(Config).
   81: 
   82: init_per_group(domain_http, Config) ->
   83:     graphql_helper:init_admin_handler(Config);
   84: init_per_group(domain_cli, Config) ->
   85:     graphql_helper:init_admin_cli(Config);
   86: init_per_group(domain_admin_tests, Config) ->
   87:     domain_helper:insert_persistent_domain(mim(), ?DOMAIN_ADMIN_EXAMPLE_DOMAIN, ?HOST_TYPE),
   88:     domain_helper:insert_domain(mim(), ?DOMAIN_ADMIN_EXAMPLE_DOMAIN, ?HOST_TYPE),
   89:     graphql_helper:init_domain_admin_handler(Config, ?DOMAIN_ADMIN_EXAMPLE_DOMAIN).
   90: 
   91: end_per_group(domain_admin_tests, _Config) ->
   92:     domain_helper:delete_domain(mim(), ?DOMAIN_ADMIN_EXAMPLE_DOMAIN),
   93:     domain_helper:delete_persistent_domain(mim(), ?DOMAIN_ADMIN_EXAMPLE_DOMAIN, ?HOST_TYPE);
   94: end_per_group(_GroupName, _Config) ->
   95:     graphql_helper:clean().
   96: 
   97: init_per_testcase(CaseName, Config) ->
   98:     escalus:init_per_testcase(CaseName, Config).
   99: 
  100: end_per_testcase(CaseName, Config) ->
  101:     escalus:end_per_testcase(CaseName, Config).
  102: 
  103: create_domain(Config) ->
  104:     create_domain(?EXAMPLE_DOMAIN, Config),
  105:     create_domain(?SECOND_EXAMPLE_DOMAIN, Config).
  106: 
  107: create_domain(DomainName, Config) ->
  108:     Result = add_domain(DomainName, ?HOST_TYPE, Config),
  109:     ParsedResult = get_ok_value([data, domain, addDomain], Result),
  110:     ?assertEqual(#{<<"domain">> => DomainName,
  111:                    <<"hostType">> => ?HOST_TYPE,
  112:                    <<"status">> => null}, ParsedResult).
  113: 
  114: unknown_host_type_error_formatting(Config) ->
  115:     DomainName = ?EXAMPLE_DOMAIN,
  116:     HostType = <<"NonExistingHostType">>,
  117:     Result = add_domain(DomainName, HostType, Config),
  118:     ?assertEqual(<<"Unknown host type">>, get_err_msg(Result)),
  119:     Result2 = get_domains_by_host_type(HostType, Config),
  120:     ?assertEqual(<<"Unknown host type">>, get_err_msg(Result2)).
  121: 
  122: add_static_domain_error_formatting(Config) ->
  123:     DomainName = <<"localhost">>,
  124:     Result = add_domain(DomainName, ?HOST_TYPE, Config),
  125:     ?assertEqual(<<"Domain is static">>, get_err_msg(Result)).
  126: 
  127: remove_static_domain_error_formatting(Config) ->
  128:     DomainName = <<"localhost">>,
  129:     Result = remove_domain(DomainName, ?HOST_TYPE, Config),
  130:     ?assertEqual(<<"Domain is static">>, get_err_msg(Result)).
  131: 
  132: enable_static_domain_error_formatting(Config) ->
  133:     DomainName = <<"localhost">>,
  134:     Result = enable_domain(DomainName, Config),
  135:     ?assertEqual(<<"Domain is static">>, get_err_msg(Result)).
  136: 
  137: disable_static_domain_error_formatting(Config) ->
  138:     DomainName = <<"localhost">>,
  139:     Result = disable_domain(DomainName, Config),
  140:     ?assertEqual(<<"Domain is static">>, get_err_msg(Result)).
  141: 
  142: domain_duplicate_error_formatting(Config) ->
  143:     DomainName = ?EXAMPLE_DOMAIN,
  144:     Result = add_domain(DomainName, ?SECOND_HOST_TYPE, Config),
  145:     ?assertMatch(<<"Domain already exists">>, get_err_msg(Result)).
  146: 
  147: domain_not_found_error_formatting_after_mutation_enable_domain(Config) ->
  148:     DomainName = <<"NonExistingDomain">>,
  149:     Result = enable_domain(DomainName, Config),
  150:     domain_not_found_error_formatting(Result).
  151: 
  152: domain_not_found_error_formatting_after_mutation_disable_domain(Config) ->
  153:     DomainName = <<"NonExistingDomain">>,
  154:     Result = disable_domain(DomainName, Config),
  155:     domain_not_found_error_formatting(Result).
  156: 
  157: domain_not_found_error_formatting_after_query(Config) ->
  158:     DomainName = <<"NonExistingDomain">>,
  159:     Result = get_domain_details(DomainName, Config),
  160:     domain_not_found_error_formatting(Result).
  161: 
  162: wrong_host_type_error_formatting(Config) ->
  163:     Result = remove_domain(?EXAMPLE_DOMAIN, ?SECOND_HOST_TYPE, Config),
  164:     ?assertEqual(<<"Wrong host type was provided">>, get_err_msg(Result)).
  165: 
  166: invalid_domain_name_error(Config) ->
  167:     %% One operation tested, because they all use the DomainName type
  168:     Result1 = add_domain(<<>>, ?HOST_TYPE, Config),
  169:     get_coercion_err_msg(Result1),
  170:     TooLong = binary:copy(<<$a>>, 1024),
  171:     Result2 = add_domain(TooLong, ?HOST_TYPE, Config),
  172:     get_coercion_err_msg(Result2).
  173: 
  174: disable_domain(Config) ->
  175:     Result = disable_domain(?EXAMPLE_DOMAIN, Config),
  176:     ParsedResult = get_ok_value([data, domain, disableDomain], Result),
  177:     ?assertMatch(#{<<"domain">> := ?EXAMPLE_DOMAIN, <<"status">> := <<"DISABLED">>}, ParsedResult),
  178:     {ok, Domain} = rpc(mim(), mongoose_domain_sql, select_domain, [?EXAMPLE_DOMAIN]),
  179:     ?assertEqual(#{host_type => ?HOST_TYPE, status => disabled}, Domain).
  180: 
  181: enable_domain(Config) ->
  182:     Result = enable_domain(?EXAMPLE_DOMAIN, Config),
  183:     ParsedResult = get_ok_value([data, domain, enableDomain], Result),
  184:     ?assertMatch(#{<<"domain">> := ?EXAMPLE_DOMAIN, <<"status">> := <<"ENABLED">>}, ParsedResult).
  185: 
  186: get_domains_by_host_type(Config) ->
  187:     Result = get_domains_by_host_type(?HOST_TYPE, Config),
  188:     ParsedResult = get_ok_value([data, domain, domainsByHostType], Result),
  189:     ?assertEqual(lists:sort([?EXAMPLE_DOMAIN, ?SECOND_EXAMPLE_DOMAIN]),
  190:                  lists:sort(ParsedResult)).
  191: 
  192: get_domain_details(Config) ->
  193:     Result = get_domain_details(?EXAMPLE_DOMAIN, Config),
  194:     ParsedResult = get_ok_value([data, domain, domainDetails], Result),
  195:     ?assertEqual(#{<<"domain">> => ?EXAMPLE_DOMAIN,
  196:                    <<"hostType">> => ?HOST_TYPE,
  197:                    <<"status">> => <<"ENABLED">>}, ParsedResult).
  198: 
  199: delete_domain(Config) ->
  200:     Result1 = remove_domain(?EXAMPLE_DOMAIN, ?HOST_TYPE, Config),
  201:     ParsedResult1 = get_ok_value([data, domain, removeDomain], Result1),
  202:     ?assertEqual(#{<<"domain">> => ?EXAMPLE_DOMAIN,
  203:                    <<"hostType">> => ?HOST_TYPE,
  204:                    <<"status">> => <<"DELETED">>},
  205:                  ParsedResult1),
  206:     Result2 = remove_domain(?EXAMPLE_DOMAIN, ?HOST_TYPE, Config),
  207:     domain_not_found_error_formatting(Result2).
  208: 
  209: request_delete_domain(Config) ->
  210:     Result1 = request_remove_domain(?SECOND_EXAMPLE_DOMAIN, ?HOST_TYPE, Config),
  211:     ParsedResult1 = get_ok_value([data, domain, requestRemoveDomain], Result1),
  212:     ?assertEqual(#{<<"domain">> => ?SECOND_EXAMPLE_DOMAIN,
  213:                    <<"hostType">> => ?HOST_TYPE,
  214:                    <<"status">> => <<"DELETING">>},
  215:                  ParsedResult1),
  216:     F = fun() ->
  217:                 Result = get_domain_details(?EXAMPLE_DOMAIN, Config),
  218:                 domain_not_found_error_formatting(Result)
  219:         end,
  220:     mongoose_helper:wait_until(F, ok, #{time_left => timer:seconds(5)}),
  221:     Result2 = request_remove_domain(?EXAMPLE_DOMAIN, ?HOST_TYPE, Config),
  222:     domain_not_found_error_formatting(Result2).
  223: 
  224: get_domains_after_deletion(Config) ->
  225:     Result = get_domains_by_host_type(?HOST_TYPE, Config),
  226:     ParsedResult = get_ok_value([data, domain, domainsByHostType], Result),
  227:     ?assertEqual([], ParsedResult).
  228: 
  229: set_domain_password(Config) ->
  230:     Result = set_domain_password(domain_helper:domain(), <<"secret">>, Config),
  231:     ParsedResult = get_ok_value([data, domain, setDomainPassword], Result),
  232:     ?assertNotEqual(nomatch, binary:match(ParsedResult, <<"successfully">>)).
  233: 
  234: set_nonexistent_domain_password(Config) ->
  235:     Domain = <<"unknown-domain.com">>,
  236:     Result = set_domain_password(Domain, <<"secret">>, Config),
  237:     domain_not_found_error_formatting(Result).
  238: 
  239: delete_domain_password(Config) ->
  240:     Result = delete_domain_password(domain_helper:domain(), Config),
  241:     ParsedResult = get_ok_value([data, domain, deleteDomainPassword], Result),
  242:     ?assertNotEqual(nomatch, binary:match(ParsedResult, <<"successfully">>)),
  243:     Result2 = delete_domain_password(domain_helper:domain(), Config),
  244:     domain_password_not_found_error_formatting(Result2).
  245: 
  246: delete_nonexistent_domain_password(Config) ->
  247:     Domain = <<"unknown-domain.com">>,
  248:     Result = delete_domain_password(Domain, Config),
  249:     domain_password_not_found_error_formatting(Result).
  250: 
  251: domain_admin_get_domain_details(Config) ->
  252:     Result = get_domain_details(?DOMAIN_ADMIN_EXAMPLE_DOMAIN, Config),
  253:     ParsedResult = get_ok_value([data, domain, domainDetails], Result),
  254:     ?assertEqual(#{<<"domain">> => ?DOMAIN_ADMIN_EXAMPLE_DOMAIN,
  255:                    <<"hostType">> => ?HOST_TYPE,
  256:                    <<"status">> => <<"ENABLED">>}, ParsedResult).
  257: 
  258: domain_admin_set_domain_password(Config) ->
  259:     Result = set_domain_password(?DOMAIN_ADMIN_EXAMPLE_DOMAIN, <<"secret">>, Config),
  260:     ParsedResult = get_ok_value([data, domain, setDomainPassword], Result),
  261:     ?assertNotEqual(nomatch, binary:match(ParsedResult, <<"successfully">>)).
  262: 
  263: domain_admin_create_domain_no_permission(Config) ->
  264:     get_unauthorized(add_domain(?EXAMPLE_DOMAIN, ?HOST_TYPE, Config)),
  265:     get_unauthorized(add_domain(?DOMAIN_ADMIN_EXAMPLE_DOMAIN, ?HOST_TYPE, Config)).
  266: 
  267: domain_admin_disable_domain_no_permission(Config) ->
  268:     get_unauthorized(disable_domain(?EXAMPLE_DOMAIN, Config)),
  269:     get_unauthorized(disable_domain(?DOMAIN_ADMIN_EXAMPLE_DOMAIN, Config)).
  270: 
  271: domain_admin_enable_domain_no_permission(Config) ->
  272:     get_unauthorized(enable_domain(?EXAMPLE_DOMAIN, Config)),
  273:     get_unauthorized(enable_domain(?DOMAIN_ADMIN_EXAMPLE_DOMAIN, Config)).
  274: 
  275: domain_admin_get_domains_by_host_type_no_permission(Config) ->
  276:     get_unauthorized(get_domains_by_host_type(?HOST_TYPE, Config)),
  277:     get_unauthorized(get_domains_by_host_type(domain_helper:host_type(), Config)).
  278: 
  279: domain_admin_get_domain_details_no_permission(Config) ->
  280:     get_unauthorized(get_domain_details(?DOMAIN_ADMIN_EXAMPLE_DOMAIN, Config)),
  281:     get_unauthorized(get_domain_details(?EXAMPLE_DOMAIN, Config)).
  282: 
  283: domain_admin_set_domain_password_no_permission(Config) ->
  284:     get_unauthorized(set_domain_password(?EXAMPLE_DOMAIN, <<"secret">>, Config)),
  285:     get_unauthorized(set_domain_password(?DOMAIN_ADMIN_EXAMPLE_DOMAIN, <<"secret">>, Config)).
  286: 
  287: domain_admin_delete_domain_no_permission(Config) ->
  288:     get_unauthorized(remove_domain(?EXAMPLE_DOMAIN, ?HOST_TYPE, Config)),
  289:     get_unauthorized(remove_domain(?DOMAIN_ADMIN_EXAMPLE_DOMAIN, ?HOST_TYPE, Config)).
  290: 
  291: domain_admin_delete_domain_password_no_permission(Config) ->
  292:     get_unauthorized(delete_domain_password(?EXAMPLE_DOMAIN, Config)),
  293:     get_unauthorized(delete_domain_password(?DOMAIN_ADMIN_EXAMPLE_DOMAIN, Config)).
  294: 
  295: %% Commands
  296: 
  297: add_domain(Domain, HostType, Config) ->
  298:     Vars = #{domain => Domain, hostType => HostType},
  299:     execute_command(<<"domain">>, <<"addDomain">>, Vars, Config).
  300: 
  301: enable_domain(Domain, Config) ->
  302:     Vars = #{domain => Domain},
  303:     execute_command(<<"domain">>, <<"enableDomain">>, Vars, Config).
  304: 
  305: disable_domain(Domain, Config) ->
  306:     Vars = #{domain => Domain},
  307:     execute_command(<<"domain">>, <<"disableDomain">>, Vars, Config).
  308: 
  309: get_domain_details(Domain, Config) ->
  310:     Vars = #{domain => Domain},
  311:     execute_command(<<"domain">>, <<"domainDetails">>, Vars, Config).
  312: 
  313: remove_domain(Domain, HostType, Config) ->
  314:     Vars = #{domain => Domain, hostType => HostType},
  315:     execute_command(<<"domain">>, <<"removeDomain">>, Vars, Config).
  316: 
  317: request_remove_domain(Domain, HostType, Config) ->
  318:     Vars = #{domain => Domain, hostType => HostType},
  319:     execute_command(<<"domain">>, <<"requestRemoveDomain">>, Vars, Config).
  320: 
  321: get_domains_by_host_type(HostType, Config) ->
  322:     Vars = #{hostType => HostType},
  323:     execute_command(<<"domain">>, <<"domainsByHostType">>, Vars, Config).
  324: 
  325: set_domain_password(Domain, Password, Config) ->
  326:     Vars = #{domain => Domain, password => Password},
  327:     execute_command(<<"domain">>, <<"setDomainPassword">>, Vars, Config).
  328: 
  329: delete_domain_password(Domain, Config) ->
  330:     Vars = #{domain => Domain},
  331:     execute_command(<<"domain">>, <<"deleteDomainPassword">>, Vars, Config).
  332: 
  333: %% Helpers
  334: 
  335: domain_not_found_error_formatting(Result) ->
  336:     ?assertMatch(<<"Given domain does not exist", _/binary>>, get_err_msg(Result)).
  337: 
  338: domain_password_not_found_error_formatting(Result) ->
  339:     ?assertEqual(<<"Domain password does not exist">>, get_err_msg(Result)).