1: %%==============================================================================
    2: %% Copyright 2019 Erlang Solutions Ltd.
    3: %%
    4: %% Licensed under the Apache License, Version 2.0 (the "License");
    5: %% you may not use this file except in compliance with the License.
    6: %% You may obtain a copy of the License at
    7: %%
    8: %% http://www.apache.org/licenses/LICENSE-2.0
    9: %%
   10: %% Unless required by applicable law or agreed to in writing, software
   11: %% distributed under the License is distributed on an "AS IS" BASIS,
   12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13: %% See the License for the specific language governing permissions and
   14: %% limitations under the License.
   15: %%==============================================================================
   16: 
   17: -module(pubsub_backend_SUITE).
   18: -compile([export_all, nowarn_export_all]).
   19: -author('michael.uvarov@erlang-solutions.com').
   20: 
   21: -include_lib("common_test/include/ct.hrl").
   22: -include_lib("exml/include/exml.hrl").
   23: -include("mongoose.hrl").
   24: -include("jlib.hrl").
   25: -include("pubsub.hrl").
   26: 
   27: %%--------------------------------------------------------------------
   28: %% Suite configuration
   29: %%--------------------------------------------------------------------
   30: 
   31: all() ->
   32:     [{group, tree_handling}].
   33: 
   34: groups() ->
   35:     [
   36:      {tree_handling, [], tree_handling_tests()}
   37:     ].
   38: 
   39: tree_handling_tests() ->
   40:     [
   41:      get_subnodes_case
   42:     ].
   43: 
   44: suite() ->
   45:     [].
   46: 
   47: %%--------------------------------------------------------------------
   48: %% Init & teardown
   49: %%--------------------------------------------------------------------
   50: 
   51: init_per_suite(Config) ->
   52:     ok = mnesia:create_schema([node()]),
   53:     mnesia:start(),
   54:     mod_pubsub_db_mnesia:init(global, #{}),
   55:     {ok, _} = application:ensure_all_started(jid),
   56:     Config.
   57: 
   58: end_per_suite(Config) ->
   59:     mod_pubsub_db_mnesia:stop(),
   60:     mnesia:stop(),
   61:     mnesia:delete_schema([node()]),
   62:     Config.
   63: 
   64: init_per_group(_GroupName, Config) ->
   65:     Config.
   66: 
   67: end_per_group(_GroupName, Config) ->
   68:     Config.
   69: 
   70: init_per_testcase(_CaseName, Config) ->
   71:     Config.
   72: 
   73: end_per_testcase(_CaseName, Config) ->
   74:     Config.
   75: 
   76: %%--------------------------------------------------------------------
   77: %% Tree handling tests
   78: %%--------------------------------------------------------------------
   79: 
   80: get_subnodes_case(_Config) ->
   81:     mod_pubsub_db_mnesia:transaction(fun() ->
   82:         get_subnodes_case_tr()
   83:         end, #{}).
   84: 
   85: get_subnodes_case_tr() ->
   86:     create_jojo_tree(),
   87:     %% Get "root"-kinda nodes
   88:     compare_lists(people_with_both_parents_unknown(), nodes_to_names(get_subnodes(<<>>))),
   89:     %% Joseph has two children
   90:     compare_lists([<<"Josuke Higashikata">>, <<"Holy Kujo">>],
   91:                   nodes_to_names(get_subnodes(<<"Joseph Joestar">>))),
   92:     %% Jotaro is a son of Holy
   93:     compare_lists([<<"Jotaro Kujo">>],
   94:                   nodes_to_names(get_subnodes(<<"Holy Kujo">>))),
   95: 
   96:     HolyDescendants = get_subnodes_tree(<<"Holy Kujo">>),
   97:     HolyAncestors = get_parentnodes_tree(<<"Holy Kujo">>),
   98: 
   99:     %% level 0 is the last item
  100:     {0, _} = lists:last(HolyDescendants),
  101:     {0, _} = lists:last(HolyAncestors),
  102: 
  103:     %% Level 0 contains the node itself
  104:     compare_lists([<<"Holy Kujo">>], nodes_to_names(proplists:get_value(0, HolyDescendants))),
  105:     compare_lists([<<"Jotaro Kujo">>], nodes_to_names(proplists:get_value(1, HolyDescendants))),
  106:     compare_lists([<<"Jolyne Cujoh">>], nodes_to_names(proplists:get_value(2, HolyDescendants))),
  107:     compare_lists([], nodes_to_names(proplists:get_value(3, HolyDescendants))),
  108:     undefined = proplists:get_value(4, HolyDescendants), %% No such item on depth 4
  109: 
  110:     compare_lists([<<"Holy Kujo">>], nodes_to_names(proplists:get_value(0, HolyAncestors))),
  111:     compare_lists([<<"Joseph Joestar">>, <<"Suzi Q Joestar">>], nodes_to_names(proplists:get_value(1, HolyAncestors))),
  112:     compare_lists([<<"George Joestar II">>, <<"Elizabeth Joestar">>], nodes_to_names(proplists:get_value(2, HolyAncestors))),
  113:     compare_lists([<<"Erina Joestar">>, <<"Jonathan Joestar">>], nodes_to_names(proplists:get_value(3, HolyAncestors))),
  114:     compare_lists([<<"George Joestar I">>, <<"Mary Joestar">>], nodes_to_names(proplists:get_value(4, HolyAncestors))),
  115:     undefined = proplists:get_value(5, HolyAncestors),
  116: 
  117:     %% Dio not found
  118:     [] = get_subnodes(<<"Dio">>),
  119:     [ {1, []}, {0, [false]} ] = get_subnodes_tree(<<"Dio">>),
  120:     [ {0, []} ] = get_parentnodes_tree(<<"Dio">>),
  121:     ok.
  122: 
  123: 
  124: %%--------------------------------------------------------------------
  125: %% Helpers
  126: %%--------------------------------------------------------------------
  127: 
  128: make_node(Name, Parents) ->
  129:     #pubsub_node{nodeid = {host(), Name}, parents = Parents}.
  130: 
  131: host() ->
  132:     <<"localhost">>.
  133: 
  134: set_node(N) ->
  135:     mod_pubsub_db_mnesia:set_node(N).
  136: 
  137: get_subnodes(Name) ->
  138:     mod_pubsub_db_mnesia:get_subnodes(host(), Name).
  139: 
  140: get_subnodes_tree(Name) ->
  141:     mod_pubsub_db_mnesia:get_subnodes_tree(host(), Name).
  142: 
  143: get_parentnodes_tree(Name) ->
  144:     mod_pubsub_db_mnesia:get_parentnodes_tree(host(), Name).
  145: 
  146: %% https://jojo.fandom.com/wiki/Joestar_Family
  147: create_jojo_tree() ->
  148:     set_node(make_node(<<"George Joestar I">>, [])),
  149:     set_node(make_node(<<"Mary Joestar">>, [])),
  150: 
  151:     set_node(make_node(<<"Jonathan Joestar">>,
  152:                        [<<"George Joestar I">>, <<"Mary Joestar">>])),
  153:     set_node(make_node(<<"Erina Joestar">>, [])),
  154: 
  155:     set_node(make_node(<<"George Joestar II">>,
  156:                        [<<"Jonathan Joestar">>, <<"Erina Joestar">>])),
  157:     set_node(make_node(<<"Elizabeth Joestar">>, [])),
  158: 
  159:     set_node(make_node(<<"Joseph Joestar">>,
  160:                        [<<"George Joestar II">>, <<"Elizabeth Joestar">>])),
  161:     set_node(make_node(<<"Tomoko Higashikata">>, [])),
  162:     set_node(make_node(<<"Suzi Q Joestar">>, [])),
  163: 
  164:     set_node(make_node(<<"Josuke Higashikata">>,
  165:                        [<<"Tomoko Higashikata">>, <<"Joseph Joestar">>])),
  166:     set_node(make_node(<<"Holy Kujo">>,
  167:                        [<<"Joseph Joestar">>, <<"Suzi Q Joestar">>])),
  168: 
  169:     set_node(make_node(<<"Sadao Kujo">>, [])),
  170: 
  171:     set_node(make_node(<<"Jotaro Kujo">>,
  172:                        [<<"Holy Kujo">>, <<"Sadao Kujo">>])),
  173: 
  174:     set_node(make_node(<<"Jolyne Cujoh">>, [<<"Jotaro Kujo">>])),
  175:     ok.
  176: 
  177: nodes_to_names(Nodes) ->
  178:     [node_to_name(N) || N <- Nodes].
  179: 
  180: node_to_name(#pubsub_node{nodeid = {_, Name}}) ->
  181:     Name.
  182: 
  183: people_with_both_parents_unknown() ->
  184:     [<<"Elizabeth Joestar">>,
  185:      <<"Erina Joestar">>,
  186:      <<"George Joestar I">>,
  187:      <<"Mary Joestar">>,
  188:      <<"Sadao Kujo">>,
  189:      <<"Suzi Q Joestar">>,
  190:      <<"Tomoko Higashikata">>].
  191: 
  192: %% Compare lists ignoring order
  193: compare_lists(L1, L2) ->
  194:     SL1 = lists:sort(L1),
  195:     SL2 = lists:sort(L2),
  196:     case SL1 of
  197:         SL2 ->
  198:             ok;
  199:         _ ->
  200:             ct:fail({compare_lists, {expected, SL1}, {provided, SL2}})
  201:     end.