1: -module(graphql_cets_SUITE).
    2: -include_lib("eunit/include/eunit.hrl").
    3: 
    4: -compile([export_all, nowarn_export_all]).
    5: 
    6: -import(distributed_helper, [mim/0, mim2/0, rpc/4]).
    7: -import(domain_helper, [host_type/1]).
    8: -import(mongooseimctl_helper, [rpc_call/3]).
    9: -import(graphql_helper, [execute_command/4, get_unauthorized/1, get_ok_value/2]).
   10: 
   11: all() ->
   12:     [{group, admin_cets_cli},
   13:      {group, admin_cets_http},
   14:      {group, domain_admin_cets}].
   15: 
   16: groups() ->
   17:     [{admin_cets_http, [parallel], admin_cets_tests()},
   18:      {admin_cets_cli, [parallel], admin_cets_tests()},
   19:      {domain_admin_cets, [], domain_admin_tests()}].
   20: 
   21: admin_cets_tests() ->
   22:     [has_sm_table_in_info,
   23:      available_nodes,
   24:      unavailable_nodes,
   25:      joined_nodes,
   26:      discovered_nodes,
   27:      remote_nodes_without_disco,
   28:      remote_nodes_with_unknown_tables,
   29:      remote_unknown_tables,
   30:      remote_nodes_with_missing_tables,
   31:      remote_missing_tables,
   32:      conflict_nodes,
   33:      conflict_tables,
   34:      discovery_works].
   35: 
   36: domain_admin_tests() ->
   37:     [domain_admin_get_table_info_test,
   38:      domain_admin_get_system_info_test].
   39: 
   40: init_per_suite(Config) ->
   41:     case rpc_call(mongoose_config, get_opt, [[internal_databases, cets, backend], undefined]) of
   42:         rdbms ->
   43:             Config1 = escalus:init_per_suite(Config),
   44:             Config2 = ejabberd_node_utils:init(mim(), Config1),
   45:             add_bad_node(),
   46:             ok = rpc_call(cets_discovery, wait_for_ready, [mongoose_cets_discovery, 5000]),
   47:             Config2 ++ distributed_helper:require_rpc_nodes([mim, mim2]);
   48:         _ ->
   49:             {skip, "CETS is not configured with RDBMS"}
   50:     end.
   51: 
   52: end_per_suite(Config) ->
   53:     ensure_bad_node_unregistered(),
   54:     escalus:end_per_suite(Config).
   55: 
   56: init_per_group(admin_cets_http, Config) ->
   57:     graphql_helper:init_admin_handler(Config);
   58: init_per_group(admin_cets_cli, Config) ->
   59:     graphql_helper:init_admin_cli(Config);
   60: init_per_group(domain_admin_cets, Config) ->
   61:     graphql_helper:init_domain_admin_handler(Config).
   62: 
   63: end_per_group(_, _Config) ->
   64:     graphql_helper:clean(),
   65:     escalus_fresh:clean().
   66: 
   67: init_per_testcase(has_sm_table_in_info, Config) ->
   68:     case rpc_call(ejabberd_sm, sm_backend, []) of
   69:         ejabberd_sm_cets ->
   70:             Config;
   71:         _ ->
   72:             {skip, "SM backend is not CETS"}
   73:     end;
   74: init_per_testcase(_, Config) ->
   75:     Config.
   76: 
   77: % Admin tests
   78: 
   79: has_sm_table_in_info(Config) ->
   80:     Res = get_table_info(Config),
   81:     Tables = get_ok_value([data, cets, tableInfo], Res),
   82:     [T] = [T || T = #{<<"tableName">> := <<"cets_session">>} <- Tables],
   83:     #{<<"memory">> := Mem, <<"nodes">> := Nodes, <<"size">> := Size} = T,
   84:     ?assert(is_integer(Mem), T),
   85:     ?assert(is_integer(Size), T),
   86:     #{node := Node1} = mim(),
   87:     assert_member(atom_to_binary(Node1), Nodes).
   88: 
   89: available_nodes(Config) ->
   90:     #{node := Node1} = mim(),
   91:     #{node := Node2} = mim2(),
   92:     Res = get_system_info(Config),
   93:     Info = get_ok_value([data, cets, systemInfo], Res),
   94:     #{<<"availableNodes">> := Nodes} = Info,
   95:     assert_member(atom_to_binary(Node1), Nodes),
   96:     assert_member(atom_to_binary(Node2), Nodes),
   97:     assert_not_member(<<"badnode@localhost">>, Nodes).
   98: 
   99: unavailable_nodes(Config) ->
  100:     #{node := Node1} = mim(),
  101:     #{node := Node2} = mim2(),
  102:     Res = get_system_info(Config),
  103:     Info = get_ok_value([data, cets, systemInfo], Res),
  104:     #{<<"unavailableNodes">> := Nodes} = Info,
  105:     assert_member(<<"badnode@localhost">>, Nodes),
  106:     assert_not_member(atom_to_binary(Node1), Nodes),
  107:     assert_not_member(atom_to_binary(Node2), Nodes).
  108: 
  109: joined_nodes(Config) ->
  110:     #{node := Node1} = mim(),
  111:     #{node := Node2} = mim2(),
  112:     Res = get_system_info(Config),
  113:     Info = get_ok_value([data, cets, systemInfo], Res),
  114:     #{<<"joinedNodes">> := Nodes} = Info,
  115:     assert_member(atom_to_binary(Node1), Nodes),
  116:     assert_member(atom_to_binary(Node2), Nodes),
  117:     assert_not_member(<<"badnode@localhost">>, Nodes).
  118: 
  119: remote_nodes_without_disco(Config) ->
  120:     Res = get_system_info(Config),
  121:     Info = get_ok_value([data, cets, systemInfo], Res),
  122:     ?assert(is_list(maps:get(<<"remoteNodesWithoutDisco">>, Info)), Info).
  123: 
  124: remote_nodes_with_unknown_tables(Config) ->
  125:     Res = get_system_info(Config),
  126:     Info = get_ok_value([data, cets, systemInfo], Res),
  127:     ?assert(is_list(maps:get(<<"remoteNodesWithUnknownTables">>, Info)), Info).
  128: 
  129: remote_unknown_tables(Config) ->
  130:     Res = get_system_info(Config),
  131:     Info = get_ok_value([data, cets, systemInfo], Res),
  132:     ?assert(is_list(maps:get(<<"remoteUnknownTables">>, Info)), Info).
  133: 
  134: remote_nodes_with_missing_tables(Config) ->
  135:     Res = get_system_info(Config),
  136:     Info = get_ok_value([data, cets, systemInfo], Res),
  137:     ?assert(is_list(maps:get(<<"remoteNodesWithMissingTables">>, Info)), Info).
  138: 
  139: remote_missing_tables(Config) ->
  140:     Res = get_system_info(Config),
  141:     Info = get_ok_value([data, cets, systemInfo], Res),
  142:     ?assert(is_list(maps:get(<<"remoteMissingTables">>, Info)), Info).
  143: 
  144: conflict_nodes(Config) ->
  145:     Res = get_system_info(Config),
  146:     Info = get_ok_value([data, cets, systemInfo], Res),
  147:     ?assertMatch(#{<<"conflictNodes">> := []}, Info).
  148: 
  149: conflict_tables(Config) ->
  150:     Res = get_system_info(Config),
  151:     Info = get_ok_value([data, cets, systemInfo], Res),
  152:     ?assertMatch(#{<<"conflictTables">> := []}, Info).
  153: 
  154: conflict_nodes_count(Config) ->
  155:     Res = get_system_info(Config),
  156:     Info = get_ok_value([data, cets, systemInfo], Res),
  157:     ?assertMatch(#{<<"conflictNodesCount">> := 0}, Info).
  158: 
  159: discovered_nodes(Config) ->
  160:     #{node := Node1} = mim(),
  161:     #{node := Node2} = mim2(),
  162:     Res = get_system_info(Config),
  163:     Info = get_ok_value([data, cets, systemInfo], Res),
  164:     #{<<"discoveredNodes">> := Nodes} = Info,
  165:     assert_member(atom_to_binary(Node1), Nodes),
  166:     assert_member(atom_to_binary(Node2), Nodes),
  167:     assert_member(<<"badnode@localhost">>, Nodes).
  168: 
  169: discovered_nodes_count(Config) ->
  170:     Res = get_system_info(Config),
  171:     Info = get_ok_value([data, cets, systemInfo], Res),
  172:     #{<<"discoveredNodesCount">> := Count} = Info,
  173:     ?assert(is_integer(Count), Info),
  174:     ?assert(Count > 2, Info).
  175: 
  176: discovery_works(Config) ->
  177:     Res = get_system_info(Config),
  178:     Info = get_ok_value([data, cets, systemInfo], Res),
  179:     ?assertMatch(#{<<"discoveryWorks">> := true}, Info).
  180: 
  181: % Domain admin tests
  182: 
  183: domain_admin_get_table_info_test(Config) ->
  184:     get_unauthorized(get_table_info(Config)).
  185: 
  186: domain_admin_get_system_info_test(Config) ->
  187:     get_unauthorized(get_system_info(Config)).
  188: 
  189: %--------------------------------------------------------------------------------------------------
  190: %                                         Helpers
  191: %--------------------------------------------------------------------------------------------------
  192: 
  193: get_table_info(Config) ->
  194:     execute_command(<<"cets">>, <<"tableInfo">>, #{}, Config).
  195: 
  196: get_system_info(Config) ->
  197:     execute_command(<<"cets">>, <<"systemInfo">>, #{}, Config).
  198: 
  199: add_bad_node() ->
  200:     ensure_bad_node_unregistered(),
  201:     register_bad_node(),
  202:     force_check(),
  203:     wait_for_has_bad_node().
  204: 
  205: register_bad_node() ->
  206:     ClusterName = <<"mim">>,
  207:     Node = <<"badnode@localhost">>,
  208:     Num = 100,
  209:     Address = <<>>,
  210:     Timestamp = rpc(mim(), mongoose_rdbms_timestamp, select, []),
  211:     InsertArgs = [ClusterName, Node, Num, Address, Timestamp],
  212:     {updated, 1} = rpc(mim(), mongoose_cets_discovery_rdbms, insert_new, InsertArgs).
  213: 
  214: ensure_bad_node_unregistered() ->
  215:     ClusterName = <<"mim">>,
  216:     Node = <<"badnode@localhost">>,
  217:     DeleteArgs = [ClusterName, Node],
  218:     %% Ensure the node is removed
  219:     {updated, _} = rpc(mim(), mongoose_cets_discovery_rdbms, delete_node_from_db, DeleteArgs).
  220: 
  221: force_check() ->
  222:     Pid = rpc(mim(), erlang, whereis, [mongoose_cets_discovery]),
  223:     true = is_pid(Pid),
  224:     Pid ! check.
  225: 
  226: has_bad_node() ->
  227:     #{unavailable_nodes := UnNodes} =
  228:         rpc(mim(), cets_discovery, system_info, [mongoose_cets_discovery]),
  229:     lists:member('badnode@localhost', UnNodes).
  230: 
  231: wait_for_has_bad_node() ->
  232:     mongoose_helper:wait_until(fun() -> has_bad_node() end, true).
  233: 
  234: assert_member(Elem, List) ->
  235:     lists:member(Elem, List)
  236:         orelse ct:fail({assert_member_failed, Elem, List}).
  237: 
  238: assert_not_member(Elem, List) ->
  239:     lists:member(Elem, List)
  240:         andalso ct:fail({assert_member_failed, Elem, List}).