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