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:start(),
   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:     Config.
   62: 
   63: init_per_group(_GroupName, Config) ->
   64:     Config.
   65: 
   66: end_per_group(_GroupName, Config) ->
   67:     Config.
   68: 
   69: init_per_testcase(_CaseName, Config) ->
   70:     Config.
   71: 
   72: end_per_testcase(_CaseName, Config) ->
   73:     Config.
   74: 
   75: %%--------------------------------------------------------------------
   76: %% Tree handling tests
   77: %%--------------------------------------------------------------------
   78: 
   79: get_subnodes_case(_Config) ->
   80:     mod_pubsub_db_mnesia:transaction(fun() ->
   81:         get_subnodes_case_tr()
   82:         end, #{}).
   83: 
   84: get_subnodes_case_tr() ->
   85:     create_jojo_tree(),
   86:     %% Get "root"-kinda nodes
   87:     compare_lists(people_with_both_parents_unknown(), nodes_to_names(get_subnodes(<<>>))),
   88:     %% Joseph has two children
   89:     compare_lists([<<"Josuke Higashikata">>, <<"Holy Kujo">>],
   90:                   nodes_to_names(get_subnodes(<<"Joseph Joestar">>))),
   91:     %% Jotaro is a son of Holy
   92:     compare_lists([<<"Jotaro Kujo">>],
   93:                   nodes_to_names(get_subnodes(<<"Holy Kujo">>))),
   94: 
   95:     HolyDescendants = get_subnodes_tree(<<"Holy Kujo">>),
   96:     HolyAncestors = get_parentnodes_tree(<<"Holy Kujo">>),
   97: 
   98:     %% level 0 is the last item
   99:     {0, _} = lists:last(HolyDescendants),
  100:     {0, _} = lists:last(HolyAncestors),
  101: 
  102:     %% Level 0 contains the node itself
  103:     compare_lists([<<"Holy Kujo">>], nodes_to_names(proplists:get_value(0, HolyDescendants))),
  104:     compare_lists([<<"Jotaro Kujo">>], nodes_to_names(proplists:get_value(1, HolyDescendants))),
  105:     compare_lists([<<"Jolyne Cujoh">>], nodes_to_names(proplists:get_value(2, HolyDescendants))),
  106:     compare_lists([], nodes_to_names(proplists:get_value(3, HolyDescendants))),
  107:     undefined = proplists:get_value(4, HolyDescendants), %% No such item on depth 4
  108: 
  109:     compare_lists([<<"Holy Kujo">>], nodes_to_names(proplists:get_value(0, HolyAncestors))),
  110:     compare_lists([<<"Joseph Joestar">>, <<"Suzi Q Joestar">>], nodes_to_names(proplists:get_value(1, HolyAncestors))),
  111:     compare_lists([<<"George Joestar II">>, <<"Elizabeth Joestar">>], nodes_to_names(proplists:get_value(2, HolyAncestors))),
  112:     compare_lists([<<"Erina Joestar">>, <<"Jonathan Joestar">>], nodes_to_names(proplists:get_value(3, HolyAncestors))),
  113:     compare_lists([<<"George Joestar I">>, <<"Mary Joestar">>], nodes_to_names(proplists:get_value(4, HolyAncestors))),
  114:     undefined = proplists:get_value(5, HolyAncestors),
  115: 
  116:     %% Dio not found
  117:     [] = get_subnodes(<<"Dio">>),
  118:     [ {1, []}, {0, [false]} ] = get_subnodes_tree(<<"Dio">>),
  119:     [ {0, []} ] = get_parentnodes_tree(<<"Dio">>),
  120:     ok.
  121: 
  122: 
  123: %%--------------------------------------------------------------------
  124: %% Helpers
  125: %%--------------------------------------------------------------------
  126: 
  127: make_node(Name, Parents) ->
  128:     #pubsub_node{nodeid = {host(), Name}, parents = Parents}.
  129: 
  130: host() ->
  131:     <<"localhost">>.
  132: 
  133: set_node(N) ->
  134:     mod_pubsub_db_mnesia:set_node(N).
  135: 
  136: get_subnodes(Name) ->
  137:     mod_pubsub_db_mnesia:get_subnodes(host(), Name).
  138: 
  139: get_subnodes_tree(Name) ->
  140:     mod_pubsub_db_mnesia:get_subnodes_tree(host(), Name).
  141: 
  142: get_parentnodes_tree(Name) ->
  143:     mod_pubsub_db_mnesia:get_parentnodes_tree(host(), Name).
  144: 
  145: %% https://jojo.fandom.com/wiki/Joestar_Family
  146: create_jojo_tree() ->
  147:     set_node(make_node(<<"George Joestar I">>, [])),
  148:     set_node(make_node(<<"Mary Joestar">>, [])),
  149: 
  150:     set_node(make_node(<<"Jonathan Joestar">>,
  151:                        [<<"George Joestar I">>, <<"Mary Joestar">>])),
  152:     set_node(make_node(<<"Erina Joestar">>, [])),
  153: 
  154:     set_node(make_node(<<"George Joestar II">>,
  155:                        [<<"Jonathan Joestar">>, <<"Erina Joestar">>])),
  156:     set_node(make_node(<<"Elizabeth Joestar">>, [])),
  157: 
  158:     set_node(make_node(<<"Joseph Joestar">>,
  159:                        [<<"George Joestar II">>, <<"Elizabeth Joestar">>])),
  160:     set_node(make_node(<<"Tomoko Higashikata">>, [])),
  161:     set_node(make_node(<<"Suzi Q Joestar">>, [])),
  162: 
  163:     set_node(make_node(<<"Josuke Higashikata">>,
  164:                        [<<"Tomoko Higashikata">>, <<"Joseph Joestar">>])),
  165:     set_node(make_node(<<"Holy Kujo">>,
  166:                        [<<"Joseph Joestar">>, <<"Suzi Q Joestar">>])),
  167: 
  168:     set_node(make_node(<<"Sadao Kujo">>, [])),
  169: 
  170:     set_node(make_node(<<"Jotaro Kujo">>,
  171:                        [<<"Holy Kujo">>, <<"Sadao Kujo">>])),
  172: 
  173:     set_node(make_node(<<"Jolyne Cujoh">>, [<<"Jotaro Kujo">>])),
  174:     ok.
  175: 
  176: nodes_to_names(Nodes) ->
  177:     [node_to_name(N) || N <- Nodes].
  178: 
  179: node_to_name(#pubsub_node{nodeid = {_, Name}}) ->
  180:     Name.
  181: 
  182: people_with_both_parents_unknown() ->
  183:     [<<"Elizabeth Joestar">>,
  184:      <<"Erina Joestar">>,
  185:      <<"George Joestar I">>,
  186:      <<"Mary Joestar">>,
  187:      <<"Sadao Kujo">>,
  188:      <<"Suzi Q Joestar">>,
  189:      <<"Tomoko Higashikata">>].
  190: 
  191: %% Compare lists ignoring order
  192: compare_lists(L1, L2) ->
  193:     SL1 = lists:sort(L1),
  194:     SL2 = lists:sort(L2),
  195:     case SL1 of
  196:         SL2 ->
  197:             ok;
  198:         _ ->
  199:             ct:fail({compare_lists, {expected, SL1}, {provided, SL2}})
  200:     end.