nickva commented on code in PR #4401:
URL: https://github.com/apache/couchdb/pull/4401#discussion_r1084728026


##########
src/chttpd/test/eunit/chttpd_changes_test.erl:
##########
@@ -0,0 +1,663 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(chttpd_changes_test).
+
+-include_lib("couch/include/couch_db.hrl").
+-include_lib("couch/include/couch_eunit.hrl").
+
+-define(USER, "chttpd_changes_test_admin").
+-define(PASS, "pass").
+-define(AUTH, {basic_auth, {?USER, ?PASS}}).
+-define(JSON, {"Content-Type", "application/json"}).
+
+-define(DOC1, <<"doc1">>).
+-define(DDC2, <<"_design/doc2">>).
+-define(DOC3, <<"doc3">>).
+-define(REVA, <<"a">>).
+-define(REVB, <<"b">>).
+-define(REVC, <<"c">>).
+-define(DELETED, true).
+-define(LEAFREV, false).
+
+% doc1 starts as rev-a, then gets 2 conflicting revisions b and c
+% ddoc2 starts as deleted at rev-a, then gets re-created as rev-c
+% doc3 starts as rev-a, then gets deleted as rev-c
+%
+test_docs() ->
+    [
+        {?DOC1, [?REVA], ?LEAFREV},
+        {?DDC2, [?REVA], ?DELETED},
+        {?DOC3, [?REVA], ?LEAFREV},
+        {?DOC1, [?REVB, ?REVA], ?LEAFREV},
+        {?DOC1, [?REVC, ?REVA], ?LEAFREV},
+        {?DOC3, [?REVB, ?REVA], ?DELETED},
+        {?DDC2, [?REVC, ?REVA], ?LEAFREV}
+    ].
+
+% Thesa are run against a Q=1, N=1 db, so we can make
+% some stronger assumptions about the exact Seq prefixes
+% returned sequences will have
+%
+changes_test_() ->
+    {
+        setup,
+        fun setup_basic/0,
+        fun teardown_basic/1,
+        with([
+            ?TDEF(t_basic),
+            ?TDEF(t_basic_post),
+            ?TDEF(t_continuous),
+            ?TDEF(t_continuous_zero_timeout),
+            ?TDEF(t_longpoll),
+            ?TDEF(t_limit_zero),
+            ?TDEF(t_continuous_limit_zero),
+            ?TDEF(t_limit_one),
+            ?TDEF(t_since_now),
+            ?TDEF(t_continuous_since_now),
+            ?TDEF(t_longpoll_since_now),
+            ?TDEF(t_style_all_docs),
+            ?TDEF(t_reverse),
+            ?TDEF(t_continuous_reverse),
+            ?TDEF(t_reverse_limit_zero),
+            ?TDEF(t_reverse_limit_one),
+            ?TDEF(t_seq_interval),
+            ?TDEF(t_selector_filter),
+            ?TDEF(t_design_filter),
+            ?TDEF(t_docs_id_filter),
+            ?TDEF(t_docs_id_filter_over_limit)
+        ])
+    }.
+
+% For Q=8 sharded dbs, unlike Q=1, we cannot make strong
+% assumptions about the exact sequence IDs for each row
+% so we'll test all the changes return and that the sequences
+% are increasing.
+%
+changes_q8_test_() ->
+    {
+        setup,
+        fun setup_q8/0,
+        fun teardown_basic/1,
+        with([
+            ?TDEF(t_basic_q8),
+            ?TDEF(t_continuous_q8),
+            ?TDEF(t_limit_zero),
+            ?TDEF(t_limit_one_q8),
+            ?TDEF(t_since_now),
+            ?TDEF(t_longpoll_since_now),
+            ?TDEF(t_reverse_q8),
+            ?TDEF(t_reverse_limit_zero),
+            ?TDEF(t_reverse_limit_one_q8),
+            ?TDEF(t_selector_filter),
+            ?TDEF(t_design_filter),
+            ?TDEF(t_docs_id_filter_q8)
+        ])
+    }.
+
+% These tests are separate as they create aditional design docs
+% as they so technically would be order dependent as the sequence
+% would keep climbing up from test to test. To avoid that run them
+% in a foreach context so setup/teardown happens for each test case.
+%
+changes_js_filters_test_() ->
+    {
+        foreach,
+        fun setup_basic/0,
+        fun teardown_basic/1,
+        [
+            ?TDEF_FE(t_js_filter),
+            ?TDEF_FE(t_js_filter_no_match),
+            ?TDEF_FE(t_js_filter_with_query_param),
+            ?TDEF_FE(t_view_filter),
+            ?TDEF_FE(t_view_filter_no_match)
+        ]
+    }.
+
+t_basic({_, DbUrl}) ->
+    Res = {Seq, Pending, Rows} = changes(DbUrl),
+    ?assertEqual(7, Seq),
+    ?assertEqual(0, Pending),
+    ?assertEqual(
+        [
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED},
+            {7, {?DDC2, <<"2-c">>}, ?LEAFREV}
+        ],
+        Rows
+    ),
+    % since=0 is the default, so it should look exactly the same
+    ?assertEqual(Res, changes(DbUrl, "?since=0")).
+
+t_basic_q8({_, DbUrl}) ->
+    {Seq, Pending, Rows} = changes(DbUrl),
+    ?assertEqual(7, Seq),
+    ?assertEqual(0, Pending),
+    {Seqs, Revs, _Deleted} = lists:unzip3(Rows),
+    ?assertEqual(
+        [
+            {?DDC2, <<"2-c">>},
+            {?DOC1, <<"2-c">>},
+            {?DOC3, <<"2-b">>}
+        ],
+        lists:sort(Revs)
+    ),
+    ?assertEqual(Seqs, lists:sort(Seqs)).
+
+t_basic_post({_, DbUrl}) ->
+    {Seq, Pending, Rows} = changes_post(DbUrl, #{}),
+    ?assertEqual(7, Seq),
+    ?assertEqual(0, Pending),
+    ?assertEqual(
+        [
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED},
+            {7, {?DDC2, <<"2-c">>}, ?LEAFREV}
+        ],
+        Rows
+    ).
+
+t_continuous({_, DbUrl}) ->
+    Params = "?feed=continuous&timeout=10",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(7, Seq),
+    ?assertEqual(0, Pending),
+    ?assertEqual(
+        [
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED},
+            {7, {?DDC2, <<"2-c">>}, ?LEAFREV}
+        ],
+        Rows
+    ).
+
+t_continuous_q8({_, DbUrl}) ->
+    Params = "?feed=continuous&timeout=10",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(7, Seq),
+    ?assertEqual(0, Pending),
+    {Seqs, Revs, _Deleted} = lists:unzip3(Rows),
+    ?assertEqual(
+        [
+            {?DDC2, <<"2-c">>},
+            {?DOC1, <<"2-c">>},
+            {?DOC3, <<"2-b">>}
+        ],
+        lists:sort(Revs)
+    ),
+    ?assertEqual(Seqs, lists:sort(Seqs)).
+
+t_continuous_zero_timeout({_, DbUrl}) ->
+    Params = "?feed=continuous&timeout=0",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(7, Seq),
+    ?assertEqual(0, Pending),
+    ?assertEqual(
+        [
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED},
+            {7, {?DDC2, <<"2-c">>}, ?LEAFREV}
+        ],
+        Rows
+    ).
+
+t_longpoll({_, DbUrl}) ->
+    Params = "?feed=longpoll",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(7, Seq),
+    ?assertEqual(0, Pending),
+    ?assertEqual(
+        [
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED},
+            {7, {?DDC2, <<"2-c">>}, ?LEAFREV}
+        ],
+        Rows
+    ).
+
+t_limit_zero({_, DbUrl}) ->
+    Params = "?limit=0",
+    ?assertEqual({0, 3, []}, changes(DbUrl, Params)).
+
+t_continuous_limit_zero({_, DbUrl}) ->
+    Params = "?feed=continuous&timeout=10&limit=0",
+    ?assertEqual({0, 3, []}, changes(DbUrl, Params)).
+
+t_limit_one({_, DbUrl}) ->
+    Params = "?limit=1",
+    ?assertEqual(
+        {5, 2, [
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV}
+        ]},
+        changes(DbUrl, Params)
+    ).
+
+t_limit_one_q8({_, DbUrl}) ->
+    Params = "?limit=1",
+    ?assertMatch(
+        {_, _, [
+            {_, {<<_/binary>>, <<_/binary>>}, _}
+        ]},
+        changes(DbUrl, Params)
+    ).
+
+t_style_all_docs({_, DbUrl}) ->
+    Params = "?style=all_docs",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(7, Seq),
+    ?assertEqual(0, Pending),
+    ?assertEqual(
+        [
+            {5, {?DOC1, [<<"2-c">>, <<"2-b">>]}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED},
+            {7, {?DDC2, <<"2-c">>}, ?LEAFREV}
+        ],
+        Rows
+    ).
+
+t_since_now({_, DbUrl}) ->
+    Params = "?since=now",
+    ?assertEqual({7, 0, []}, changes(DbUrl, Params)).
+
+t_continuous_since_now({_, DbUrl}) ->
+    Params = "?feed=continuous&timeout=10&since=now",
+    ?assertEqual({7, 0, []}, changes(DbUrl, Params)).
+
+t_longpoll_since_now({_, DbUrl}) ->
+    Params = "?feed=longpoll&timeout=10&since=now",
+    ?assertEqual({7, 0, []}, changes(DbUrl, Params)).
+
+t_reverse({_, DbUrl}) ->
+    Params = "?descending=true",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(5, Seq),
+    ?assertEqual(-3, Pending),
+    ?assertEqual(
+        [
+            {7, {?DDC2, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED},
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV}
+        ],
+        Rows
+    ).
+
+t_continuous_reverse({_, DbUrl}) ->
+    Params = "?feed=continuous&timeout=10&descending=true",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(5, Seq),
+    ?assertEqual(-3, Pending),
+    ?assertEqual(
+        [
+            {7, {?DDC2, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED},
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV}
+        ],
+        Rows
+    ).
+
+t_reverse_q8({_, DbUrl}) ->
+    Params = "?descending=true",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(7, Seq),
+    ?assertEqual(-3, Pending),
+    {Seqs, Revs, _Deleted} = lists:unzip3(Rows),
+    ?assertEqual(
+        [
+            {?DDC2, <<"2-c">>},
+            {?DOC1, <<"2-c">>},
+            {?DOC3, <<"2-b">>}
+        ],
+        lists:sort(Revs)
+    ),
+    ?assertEqual(Seqs, lists:sort(Seqs)).
+
+t_reverse_limit_zero({_, DbUrl}) ->
+    Params = "?descending=true&limit=0",
+    ?assertEqual({7, 0, []}, changes(DbUrl, Params)).
+
+t_reverse_limit_one({_, DbUrl}) ->
+    Params = "?descending=true&limit=1",
+    ?assertEqual(
+        {7, -1, [
+            {7, {?DDC2, <<"2-c">>}, ?LEAFREV}
+        ]},
+        changes(DbUrl, Params)
+    ).
+
+t_reverse_limit_one_q8({_, DbUrl}) ->
+    Params = "?descending=true&limit=1",
+    ?assertMatch(
+        {7, -1, [
+            {_, {<<_/binary>>, <<_/binary>>}, _}
+        ]},
+        changes(DbUrl, Params)
+    ).
+
+t_seq_interval({_, DbUrl}) ->
+    Params = "?seq_interval=3",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(7, Seq),
+    ?assertEqual(0, Pending),
+    ?assertEqual(
+        [
+            {null, {?DOC1, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED},
+            {null, {?DDC2, <<"2-c">>}, ?LEAFREV}
+        ],
+        Rows
+    ).
+
+t_selector_filter({_, DbUrl}) ->
+    Params = "?filter=_selector",
+    Body = #{<<"selector">> => #{<<"_id">> => ?DOC1}},
+    {Seq, Pending, Rows} = changes_post(DbUrl, Body, Params),
+    ?assertEqual(7, Seq),
+    ?assertEqual(0, Pending),
+    ?assertMatch([{_, {?DOC1, <<"2-c">>}, ?LEAFREV}], Rows).
+
+t_design_filter({_, DbUrl}) ->
+    Params = "?filter=_design",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(7, Seq),
+    ?assertEqual(2, Pending),
+    ?assertMatch([{_, {?DDC2, <<"2-c">>}, ?LEAFREV}], Rows).
+
+t_docs_id_filter({_, DbUrl}) ->
+    Params = "?filter=_doc_ids",
+    Body = #{<<"doc_ids">> => [?DOC3, ?DOC1]},
+    meck:reset(couch_changes),
+    {_, _, Rows} = changes_post(DbUrl, Body, Params),
+    ?assertEqual(1, meck:num_calls(couch_changes, send_changes_doc_ids, 6)),
+    ?assertEqual(
+        [
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED}
+        ],
+        Rows
+    ).
+
+t_docs_id_filter_q8({_, DbUrl}) ->
+    Params = "?filter=_doc_ids",
+    Body = #{<<"doc_ids">> => [?DOC3, ?DOC1]},
+    {_, _, Rows} = changes_post(DbUrl, Body, Params),
+    {Seqs, Revs, _Deleted} = lists:unzip3(Rows),
+    ?assertEqual(
+        [
+            {?DOC1, <<"2-c">>},
+            {?DOC3, <<"2-b">>}
+        ],
+        lists:sort(Revs)
+    ),
+    ?assertEqual(Seqs, lists:sort(Seqs)).
+
+t_docs_id_filter_over_limit({_, DbUrl}) ->
+    Params = "?filter=_doc_ids",
+    Body = #{<<"doc_ids">> => [<<"missingdoc">>, ?DOC3, <<"notthere">>, 
?DOC1]},
+    meck:reset(couch_changes),
+    {_, _, Rows} = changes_post(DbUrl, Body, Params),
+    ?assertEqual(0, meck:num_calls(couch_changes, send_changes_doc_ids, 6)),
+    ?assertEqual(
+        [
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED}
+        ],
+        Rows
+    ).
+
+t_js_filter({_, DbUrl}) ->
+    DDocId = "_design/filters",
+    FilterFun = <<"function(doc, req) {return (doc._id == 'doc3')}">>,
+    DDoc = #{<<"filters">> => #{<<"f">> => FilterFun}},
+    DDocUrl = DbUrl ++ "/" ++ DDocId,
+    {_, #{<<"rev">> := Rev, <<"ok">> := true}} = req(put, DDocUrl, DDoc),
+    Params = "?filter=filters/f",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(8, Seq),
+    ?assertEqual(0, Pending),
+    ?assertEqual(
+        [
+            {6, {?DOC3, <<"2-b">>}, ?DELETED}
+        ],
+        Rows
+    ),
+    {200, #{}} = req(delete, DDocUrl ++ "?rev=" ++ binary_to_list(Rev)).
+
+t_js_filter_no_match({_, DbUrl}) ->
+    DDocId = "_design/filters",
+    FilterFun = <<"function(doc, req) {return false}">>,
+    DDoc = #{<<"filters">> => #{<<"f">> => FilterFun}},
+    DDocUrl = DbUrl ++ "/" ++ DDocId,
+    {_, #{<<"rev">> := Rev, <<"ok">> := true}} = req(put, DDocUrl, DDoc),
+    Params = "?filter=filters/f",
+    ?assertEqual({8, 0, []}, changes(DbUrl, Params)),
+    {200, #{}} = req(delete, DDocUrl ++ "?rev=" ++ binary_to_list(Rev)).
+
+t_js_filter_with_query_param({_, DbUrl}) ->
+    DDocId = "_design/filters",
+    FilterFun = <<"function(doc, req) {return (req.query.yup == 1)}">>,
+    DDoc = #{<<"filters">> => #{<<"f">> => FilterFun}},
+    DDocUrl = DbUrl ++ "/" ++ DDocId,
+    {_, #{<<"rev">> := Rev, <<"ok">> := true}} = req(put, DDocUrl, DDoc),
+    Params = "?filter=filters/f&yup=1",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(8, Seq),
+    ?assertEqual(0, Pending),
+    ?assertMatch(
+        [
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV},
+            {6, {?DOC3, <<"2-b">>}, ?DELETED},
+            {7, {?DDC2, <<"2-c">>}, ?LEAFREV},
+            {8, {<<"_design/filters">>, <<"1-", _/binary>>}, ?LEAFREV}
+        ],
+        Rows
+    ),
+    {200, #{}} = req(delete, DDocUrl ++ "?rev=" ++ binary_to_list(Rev)).
+
+t_view_filter({_, DbUrl}) ->
+    DDocId = "_design/views",
+    ViewFun = <<"function(doc) {if (doc._id == 'doc1') {emit(1, 1);}}">>,
+    DDoc = #{<<"views">> => #{<<"v">> => #{<<"map">> => ViewFun}}},
+    DDocUrl = DbUrl ++ "/" ++ DDocId,
+    {_, #{<<"rev">> := Rev, <<"ok">> := true}} = req(put, DDocUrl, DDoc),
+    Params = "?filter=_view&view=views/v",
+    {Seq, Pending, Rows} = changes(DbUrl, Params),
+    ?assertEqual(8, Seq),
+    ?assertEqual(0, Pending),
+    ?assertEqual(
+        [
+            {5, {?DOC1, <<"2-c">>}, ?LEAFREV}
+        ],
+        Rows
+    ),
+    {200, #{}} = req(delete, DDocUrl ++ "?rev=" ++ binary_to_list(Rev)).
+
+t_view_filter_no_match({_, DbUrl}) ->
+    DDocId = "_design/views",
+    ViewFun = <<"function(doc) {if (doc._id == 'docX') {emit(1, 1);}}">>,
+    DDoc = #{<<"views">> => #{<<"v">> => #{<<"map">> => ViewFun}}},
+    DDocUrl = DbUrl ++ "/" ++ DDocId,
+    {_, #{<<"rev">> := Rev, <<"ok">> := true}} = req(put, DDocUrl, DDoc),
+    Params = "?filter=_view&view=views/v",
+    ?assertEqual({8, 0, []}, changes(DbUrl, Params)),
+    {200, #{}} = req(delete, DDocUrl ++ "?rev=" ++ binary_to_list(Rev)).
+
+post_doc_ids(DbUrl, Body) ->

Review Comment:
   Sorry. That was a left-over from the benchmark eunit test. Removed it. 
Thanks!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to