1: %% @doc This suite tests API of accumulator encapsulated in mongoose_acc module
    2: -module(acc_SUITE).
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -include_lib("exml/include/exml.hrl").
    6: -include_lib("eunit/include/eunit.hrl").
    7: -include("ejabberd_commands.hrl").
    8: -include("jlib.hrl").
    9: -include("mongoose.hrl").
   10: 
   11: -define(PRT(X, Y), ct:pal("~p: ~p", [X, Y])).
   12: -define(ACC_PARAMS, #{location => ?LOCATION,
   13:                       host_type => <<"local host">>,
   14:                       lserver => <<"localhost">>}).
   15: 
   16: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   17: %%%% suite configuration
   18: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   19: 
   20: all() ->
   21:     [
   22:      {group, basic}
   23:     ].
   24: 
   25: groups() ->
   26:     [
   27:      {basic, [sequence],
   28:       [
   29:        store_retrieve_and_delete,
   30:        store_retrieve_and_delete_many,
   31:        init_from_element,
   32:        produce_iq_meta_automatically,
   33:        strip,
   34:        strip_with_params,
   35:        parse_with_cdata
   36:       ]
   37:      }
   38:     ].
   39: 
   40: init_per_suite(C) ->
   41:     application:ensure_all_started(jid),
   42:     C.
   43: 
   44: end_per_suite(C) ->
   45:     C.
   46: 
   47: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   48: %%%% test methods
   49: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   50: 
   51: store_retrieve_and_delete(_C) ->
   52:     Acc = mongoose_acc:new(?ACC_PARAMS),
   53:     Acc2 = mongoose_acc:set(ns, check, 1, Acc),
   54:     ?assertEqual(1, mongoose_acc:get(ns, check, Acc2)),
   55:     Acc3 = mongoose_acc:set(ns, check, 2, Acc2),
   56:     ?assertEqual(2, mongoose_acc:get(ns, check, Acc3)),
   57:     Acc4 = mongoose_acc:delete(ns, check, Acc3),
   58:     ?assertError(_, mongoose_acc:get(ns, check, Acc4)),
   59:     ok.
   60: 
   61: store_permanent_retrieve_and_delete(_C) ->
   62:     Acc = mongoose_acc:new(?ACC_PARAMS),
   63:     Acc2 = mongoose_acc:set_permanent(ns, check, 1, Acc),
   64:     ?assertEqual(1, mongoose_acc:get(ns, check, Acc2)),
   65:     Acc3 = mongoose_acc:set_permanent(ns, check, 2, Acc2),
   66:     ?assertEqual(2, mongoose_acc:get(ns, check, Acc3)),
   67:     ?assertEqual([{ns, check}], mongoose_acc:get_permanent_keys(Acc3)),
   68:     Acc4 = mongoose_acc:delete(ns, check, Acc3),
   69:     ?assertError(_, mongoose_acc:get(ns, check, Acc4)),
   70:     ?assertEqual([], mongoose_acc:get_permanent_keys(Acc4)),
   71:     ok.
   72: 
   73: store_retrieve_and_delete_many(_C) ->
   74:     Acc = mongoose_acc:new(?ACC_PARAMS),
   75:     KV = [{check, 1}, {check2, 2}, {check3, 3}],
   76:     Acc2 = mongoose_acc:set(ns, [{check3, 0} | KV], Acc),
   77:     [?assertEqual(V, mongoose_acc:get(ns, K, Acc2)) || {K, V} <- KV],
   78:     NS = mongoose_acc:get(ns, Acc2),
   79:     ?assertEqual(lists:sort(NS), lists:sort(KV)),
   80:     Acc3 = mongoose_acc:delete_many(ns, [K || {K, _} <- KV], Acc2),
   81:     [?assertError(_, mongoose_acc:get(ns, K, Acc3)) || {K, _} <- KV],
   82:     ?assertEqual([], mongoose_acc:get(ns, Acc3)),
   83:     Acc4 = mongoose_acc:delete(ns, Acc2),
   84:     [?assertError(_, mongoose_acc:get(ns, K, Acc4)) || {K, _} <- KV],
   85:     ?assertEqual([], mongoose_acc:get(ns, Acc4)),
   86:     ok.
   87: 
   88: store_permanent_retrieve_and_delete_many(_C) ->
   89:     Acc = mongoose_acc:new(?ACC_PARAMS),
   90:     KV = [{check, 1}, {check2, 2}, {check3, 3}],
   91:     NK = [{ns, K} || {K, _} <- KV],
   92:     Acc2 = mongoose_acc:set_permanent(ns, [{check3, 0} | KV], Acc),
   93:     [?assertEqual(V, mongoose_acc:get(ns, K, Acc2)) || {K, V} <- KV],
   94:     NS = mongoose_acc:get(ns, Acc2),
   95:     ?assertEqual(lists:sort(NS), lists:sort(KV)),
   96:     ?assertEqual(lists:sort(NK), lists:sort(mongoose_acc:get_permanent_keys(Acc2))),
   97:     Acc3 = mongoose_acc:delete_many(ns, [K || {K, _} <- KV], Acc2),
   98:     [?assertError(_, mongoose_acc:get(ns, K, Acc3)) || {K, _} <- KV],
   99:     ?assertEqual([], mongoose_acc:get(ns, Acc3)),
  100:     ?assertEqual([], mongoose_acc:get_permanent_keys(Acc3)),
  101:     Acc4 = mongoose_acc:delete(ns, Acc2),
  102:     [?assertError(_, mongoose_acc:get(ns, K, Acc4)) || {K, _} <- KV],
  103:     ?assertEqual([], mongoose_acc:get(ns, Acc4)),
  104:     ?assertEqual([], mongoose_acc:get_permanent_keys(Acc4)),
  105:     ok.
  106: 
  107: init_from_element(_C) ->
  108:     Acc = mongoose_acc:new(?ACC_PARAMS#{element => sample_stanza()}),
  109:     ?PRT("Acc", Acc),
  110:     ?assertEqual(<<"iq">>, mongoose_acc:stanza_name(Acc)),
  111:     ?assertEqual(<<"set">>, mongoose_acc:stanza_type(Acc)),
  112:     ok.
  113: 
  114: 
  115: produce_iq_meta_automatically(_C) ->
  116:     Acc = mongoose_acc:new(?ACC_PARAMS#{element => sample_stanza()}),
  117:     {Command, Acc1} = mongoose_iq:command(Acc),
  118:     ?assertEqual(<<"block">>, Command),
  119:     % We check for exactly the same Acc, as there is no need to update the cache
  120:     {XMLNS, Acc1} = mongoose_iq:xmlns(Acc1),
  121:     ?assertEqual(<<"urn:xmpp:blocking">>, XMLNS),
  122:     Iq = mongoose_acc:update_stanza(#{ element => iq_stanza() }, Acc1),
  123:     {IqData, _Iq1} = mongoose_iq:info(Iq),
  124:     ?assertEqual(set, IqData#iq.type),
  125:     ?assertEqual(<<"urn:ietf:params:xml:ns:xmpp-session">>, IqData#iq.xmlns),
  126:     ok.
  127: 
  128: parse_with_cdata(_C) ->
  129:     Acc = mongoose_acc:new(?ACC_PARAMS#{element => stanza_with_cdata()}),
  130:     {XMLNS, _} = mongoose_iq:xmlns(Acc),
  131:     ?assertEqual(<<"jabber:iq:roster">>, XMLNS).
  132: 
  133: strip(_C) ->
  134:     El = iq_stanza(),
  135:     FromJID = jid:make(<<"jajid">>, <<"localhost">>, <<>>),
  136:     ToJID = jid:make(<<"tyjid">>, <<"localhost">>, <<>>),
  137:     Server = maps:get(lserver, ?ACC_PARAMS),
  138:     HostType = maps:get(host_type, ?ACC_PARAMS),
  139:     Acc = mongoose_acc:new(?ACC_PARAMS#{element => El,
  140:                                         from_jid => FromJID,
  141:                                         to_jid => ToJID}),
  142:     {XMLNS1, Acc1} = mongoose_iq:xmlns(Acc),
  143:     ?assertEqual(<<"urn:ietf:params:xml:ns:xmpp-session">>, XMLNS1),
  144:     ?assertEqual(<<"set">>, mongoose_acc:stanza_type(Acc1)),
  145:     ?assertEqual(undefined, mongoose_acc:get(ns, ppp, undefined, Acc1)),
  146:     Acc2 = mongoose_acc:set_permanent(ns, ppp, 997, Acc1),
  147:     Acc3 = mongoose_acc:set(ns2, [{a, 1}, {b, 2}], Acc2),
  148:     ?assertMatch([_, _], mongoose_acc:get(ns2, Acc3)),
  149:     ?assertEqual(Server, mongoose_acc:lserver(Acc3)),
  150:     ?assertEqual(HostType, mongoose_acc:host_type(Acc3)),
  151:     ?assertEqual({FromJID, ToJID, El}, mongoose_acc:packet(Acc3)),
  152:     Ref = mongoose_acc:ref(Acc3),
  153:     %% strip stanza and check that only non-permanent fields are missing
  154:     NAcc1 = mongoose_acc:strip(Acc3),
  155:     {XMLNS3, NAcc2} = mongoose_iq:xmlns(NAcc1),
  156:     ?assertEqual(<<"urn:ietf:params:xml:ns:xmpp-session">>, XMLNS3),
  157:     ?assertEqual(<<"set">>, mongoose_acc:stanza_type(NAcc2)),
  158:     ?assertEqual(Server, mongoose_acc:lserver(NAcc2)),
  159:     ?assertEqual(HostType, mongoose_acc:host_type(NAcc2)),
  160:     ?assertEqual({FromJID, ToJID, El}, mongoose_acc:packet(NAcc2)),
  161:     ?assertEqual(Ref, mongoose_acc:ref(NAcc2)),
  162:     ?assertEqual(997, mongoose_acc:get(ns, ppp, NAcc2)),
  163:     ?assertEqual([], mongoose_acc:get(ns2, NAcc2)).
  164: 
  165: strip_with_params(_Config) ->
  166:     FromJID = jid:make(<<"jajid">>, <<"localhost">>, <<>>),
  167:     ToJID = jid:make(<<"tyjid">>, <<"localhost">>, <<>>),
  168:     Server = maps:get(lserver, ?ACC_PARAMS),
  169:     HostType = maps:get(host_type, ?ACC_PARAMS),
  170:     Acc = mongoose_acc:new(?ACC_PARAMS#{element => iq_stanza(),
  171:                                         from_jid => FromJID,
  172:                                         to_jid => ToJID}),
  173:     {XMLNS1, Acc1} = mongoose_iq:xmlns(Acc),
  174:     ?assertEqual(<<"urn:ietf:params:xml:ns:xmpp-session">>, XMLNS1),
  175:     ?assertEqual(<<"set">>, mongoose_acc:stanza_type(Acc1)),
  176:     ?assertEqual(undefined, mongoose_acc:get(ns, ppp, undefined, Acc1)),
  177:     Acc2 = mongoose_acc:set_permanent(ns, ppp, 997, Acc1),
  178:     Acc3 = mongoose_acc:set(ns2, [{a, 1}, {b, 2}], Acc2),
  179:     ?assertMatch([_, _], mongoose_acc:get(ns2, Acc3)),
  180:     ?assertEqual(Server, mongoose_acc:lserver(Acc3)),
  181:     ?assertEqual(HostType, mongoose_acc:host_type(Acc3)),
  182:     Ref = mongoose_acc:ref(Acc3),
  183:     %% strip stanza with params and check that new params are applied
  184:     %% and non-permanent fields are missing
  185:     NewServer = <<"test.", Server/binary>>,
  186:     NewHostType = <<"new ", HostType/binary>>,
  187:     NewStanza = sample_stanza(),
  188:     StripParams = #{lserver => NewServer,
  189:                     host_type => NewHostType,
  190:                     element => NewStanza},
  191:     NAcc1 = mongoose_acc:strip(StripParams, Acc3),
  192:     {XMLNS2, NAcc2} = mongoose_iq:xmlns(NAcc1),
  193:     ?assertEqual(<<"urn:xmpp:blocking">>, XMLNS2),
  194:     ?assertEqual(jid:from_binary(<<"a@localhost">>), mongoose_acc:to_jid(NAcc2)),
  195:     ?assertEqual(Ref, mongoose_acc:ref(NAcc2)),
  196:     ?assertEqual(997, mongoose_acc:get(ns, ppp, NAcc2)),
  197:     ?assertEqual([], mongoose_acc:get(ns2, NAcc2)),
  198:     ?assertEqual(sample_stanza(), mongoose_acc:element(NAcc2)),
  199:     ?assertEqual(NewServer, mongoose_acc:lserver(NAcc2)),
  200:     ?assertEqual(NewHostType, mongoose_acc:host_type(NAcc2)).
  201: 
  202: sample_stanza() ->
  203:     {xmlel, <<"iq">>,
  204:         [{<<"xml:lang">>, <<"en">>},
  205:          {<<"type">>, <<"set">>},
  206:          {<<"from">>, <<"a@localhost">>},
  207:          {<<"to">>, <<"a@localhost">>}],
  208:         [{xmlel, <<"block">>,
  209:             [{<<"xmlns">>, <<"urn:xmpp:blocking">>}],
  210:             [{xmlel, <<"item">>,
  211:                 [{<<"jid">>, <<"bob37.814302@localhost">>}],
  212:                 []}]}]}.
  213: 
  214: 
  215: stanza_with_cdata() ->
  216:     Txt = <<"<iq type=\"get\" id=\"aab9a\" from=\"a@localhost\" to=\"a@localhost\">"
  217:             "<query xmlns=\"jabber:iq:roster\"/>\" \"</iq>">>,
  218:     {ok, X} = exml:parse(Txt),
  219:     X.
  220: 
  221: 
  222: iq_stanza() ->
  223:     {xmlel,<<"iq">>,
  224:         [{<<"type">>,<<"set">>},
  225:          {<<"id">>,<<"a31baa4c478896af19b76bac799b65ed">>},
  226:          {<<"from">>, <<"a@localhost">>},
  227:          {<<"to">>, <<"localhost">>}],
  228:         [{xmlel,<<"session">>,
  229:             [{<<"xmlns">>,<<"urn:ietf:params:xml:ns:xmpp-session">>}],
  230:             []}]}.
  231: 
  232: another_iq_stanza() ->
  233:     {xmlel,<<"iq">>,
  234:         [{<<"type">>,<<"pet">>},
  235:          {<<"id">>,<<"a31baa4c478896af19b76bac799b65ed">>},
  236:          {<<"from">>, <<"a@localhost">>},
  237:          {<<"to">>, <<"localhost">>}],
  238:         [{xmlel,<<"session">>,
  239:             [{<<"xmlns">>,<<"urn:ietf:params:xml:ns:xmpp-session">>}],
  240:             []}]}.