pgj commented on code in PR #4782:
URL: https://github.com/apache/couchdb/pull/4782#discussion_r1344616078


##########
src/mango/src/mango_cursor_text.erl:
##########
@@ -334,4 +350,805 @@ get_json_docs(DbName, Hits) ->
         Hits
     ).
 
+%%%%%%%% module tests %%%%%%%%
+
+-ifdef(TEST).
+-include_lib("couch/include/couch_eunit.hrl").
+
+% This behavior needs to be revisited and potentially fixed, the tests
+% here only to record the current version.
+
+create_test_() ->
+    {
+        foreach,
+        fun() ->
+            meck:expect(couch_db, name, [db], meck:val(db_name))
+        end,
+        fun(_) ->
+            meck:unload()
+        end,
+        [
+            ?TDEF_FE(t_create_no_indexes),
+            ?TDEF_FE(t_create_multiple_indexes),
+            ?TDEF_FE(t_create_regular),
+            ?TDEF_FE(t_create_no_bookmark),
+            ?TDEF_FE(t_create_invalid_bookmark)
+        ]
+    }.
+
+t_create_no_indexes(_) ->
+    Exception = {mango_error, mango_cursor_text, multiple_text_indexes},
+    ?assertThrow(Exception, create(db, {[], trace}, selector, options)).
+
+t_create_multiple_indexes(_) ->
+    Indexes = [index1, index2, index3],
+    Exception = {mango_error, mango_cursor_text, multiple_text_indexes},
+    ?assertThrow(Exception, create(db, {Indexes, trace}, selector, options)).
+
+t_create_regular(_) ->
+    Index = #idx{type = <<"text">>},
+    Indexes = [Index],
+    Trace = #{},
+    Limit = 10,
+    Options = [{limit, Limit}, {skip, skip}, {fields, fields}, {bookmark, 
bookmark}],
+    Options1 = [{limit, Limit}, {skip, skip}, {fields, fields}, {bookmark, 
unpacked_bookmark}],
+    Cursor = #cursor{
+        db = db,
+        index = Index,
+        ranges = null,
+        trace = Trace,
+        selector = selector,
+        opts = Options1,
+        limit = Limit,
+        skip = skip,
+        fields = fields
+    },
+    meck:expect(dreyfus_bookmark, unpack, [db_name, bookmark], 
meck:val(unpacked_bookmark)),
+    ?assertEqual({ok, Cursor}, create(db, {Indexes, Trace}, selector, 
Options)).
+
+t_create_no_bookmark(_) ->
+    Limit = 99,
+    Options = [{limit, Limit}, {skip, skip}, {fields, fields}, {bookmark, 
nil}],
+    Options1 = [{limit, Limit}, {skip, skip}, {fields, fields}, {bookmark, 
[]}],
+    Cursor = #cursor{
+        db = db,
+        index = index,
+        ranges = null,
+        trace = trace,
+        selector = selector,
+        opts = Options1,
+        limit = Limit,
+        skip = skip,
+        fields = fields
+    },
+    ?assertEqual({ok, Cursor}, create(db, {[index], trace}, selector, 
Options)).
+
+t_create_invalid_bookmark(_) ->
+    Options = [{bookmark, invalid}],
+    Exception = {mango_error, mango_cursor_text, {invalid_bookmark, invalid}},
+    meck:expect(dreyfus_bookmark, unpack, [db_name, invalid], 
meck:raise(error, something)),
+    ?assertThrow(Exception, create(db, {[index], trace}, selector, Options)).
+
+execute_test_() ->
+    {
+        foreach,
+        fun() ->
+            meck:new(foo, [non_strict]),
+            meck:expect(couch_db, name, [db], meck:val(db_name)),
+            meck:expect(couch_stats, increment_counter, [[mango, 
docs_examined]], meck:val(ok)),
+            % Dummy mock functions to progressively update the
+            % respective states therefore their results could be
+            % asserted later on.
+            meck:expect(
+                dreyfus_bookmark,
+                pack,
+                fun(Bookmark) ->
+                    case Bookmark of
+                        nil -> null;
+                        [bookmark, N] -> [bookmark, N]
+                    end
+                end
+            ),
+            meck:expect(
+                dreyfus_bookmark,
+                update,
+                fun(_Sort, [bookmark, N], [#sortable{}]) -> [bookmark, N + 1] 
end
+            ),
+            meck:expect(
+                dreyfus_fabric,
+                get_json_docs,
+                fun(db_name, Ids) ->
+                    IdDocs = lists:flatmap(
+                        fun({id, N} = Id) ->
+                            case N of
+                                not_found -> [];
+                                _ -> [{Id, {doc, {doc, N}}}]
+                            end
+                        end,
+                        Ids
+                    ),
+                    {ok, IdDocs}
+                end
+            ),
+            meck:expect(mango_execution_stats, log_start, [stats], 
meck:val({stats, 0})),
+            meck:expect(
+                mango_execution_stats,
+                maybe_add_stats,
+                fun(_Options, _UserFun, {stats, N}, {acc, M}) -> {{acc, M + 
1}, {stats, N + 1}} end
+            ),
+            meck:expect(mango_execution_stats, log_stats, [{stats, '_'}], 
meck:val(ok)),
+            meck:expect(
+                mango_cursor,
+                maybe_add_warning,
+                fun(_UserFun, _Cursor, {stats, _N}, {acc, M}) -> {acc, M + 1} 
end
+            ),
+            meck:expect(
+                mango_execution_stats,
+                incr_docs_examined,
+                fun({stats, N}) -> {stats, N + 1} end
+            ),
+            meck:expect(
+                mango_execution_stats,
+                incr_results_returned,
+                fun({stats, N}) -> {stats, N + 1} end
+            ),
+            meck:expect(
+                mango_selector_text,
+                append_sort_type,
+                fun(RawField, selector) -> <<RawField/binary, "<type>">> end
+            ),
+            meck:expect(mango_doc, get_field, fun({doc, N}, <<"_id">>) -> N 
end),
+            meck:expect(mango_fields, extract, fun({doc, N}, fields) -> 
{final_doc, N} end),
+            meck:expect(
+                foo,
+                add_key_only,
+                fun({add_key, bookmark, [bookmark, _M]}, {acc, N}) -> {ok, 
{acc, N + 1}} end
+            ),
+            meck:expect(
+                foo,
+                normal,
+                fun(Args, {acc, N}) ->
+                    case Args of
+                        {row, {final_doc, _M}} -> ok;
+                        {add_key, bookmark, [bookmark, _M]} -> ok
+                    end,
+                    {ok, {acc, N + 1}}
+                end
+            )
+        end,
+        fun(_) ->
+            meck:unload()
+        end,
+        [
+            ?TDEF_FE(t_execute_empty, 10),
+            ?TDEF_FE(t_execute_no_results, 10),
+            ?TDEF_FE(t_execute_more_results, 10),
+            ?TDEF_FE(t_execute_unique_results, 10),
+            ?TDEF_FE(t_execute_limit_cutoff, 10),
+            ?TDEF_FE(t_execute_limit_cutoff_unique, 10),
+            ?TDEF_FE(t_execute_limit_zero, 10),
+            ?TDEF_FE(t_execute_skip, 10),
+            ?TDEF_FE(t_execute_skip_unique, 10),
+            ?TDEF_FE(t_execute_no_matches, 10),
+            ?TDEF_FE(t_execute_mixed_matches, 10),
+            ?TDEF_FE(t_execute_user_fun_returns_stop, 10),
+            ?TDEF_FE(t_execute_search_error, 10)
+        ]
+    }.
+
+t_execute_empty(_) ->
+    Options = [{partition, partition}, {sort, {[]}}, {bookmark, [bookmark, 
0]}],
+    Cursor = #cursor{
+        db = db,
+        index = #idx{ddoc = <<"ddoc">>},
+        limit = limit,
+        skip = skip,
+        fields = fields,
+        selector = selector,
+        opts = Options,
+        execution_stats = stats
+    },
+    meck:expect(mango_selector_text, convert, [selector], meck:val(<<>>)),
+    ?assertEqual({ok, {acc, 3}}, execute(Cursor, fun foo:add_key_only/2, {acc, 
0})),
+    ?assertEqual(0, meck:num_calls(couch_stats, increment_counter, 1)),
+    ?assertEqual(0, meck:num_calls(mango_execution_stats, incr_docs_examined, 
1)),
+    ?assertEqual(0, meck:num_calls(mango_execution_stats, 
incr_results_returned, 1)).
+
+t_execute_no_results(_) ->
+    Limit = 10,
+    Skip = 0,
+    IdxDDoc = <<"ddoc">>,
+    IdxName = <<"index">>,
+    Sort = [<<"field1">>, <<"field2">>],
+    Options = [{partition, partition}, {sort, {Sort}}, {bookmark, [bookmark, 
0]}],
+    Cursor = #cursor{
+        db = db,
+        index = #idx{ddoc = IdxDDoc, name = IdxName},
+        limit = Limit,
+        skip = Skip,
+        fields = fields,
+        selector = selector,
+        opts = Options,
+        execution_stats = stats
+    },
+    QueryArgs =
+        #index_query_args{
+            q = query,
+            partition = partition,
+            limit = Skip + Limit,
+            bookmark = [bookmark, 0],
+            sort = [<<"field1<type>">>, <<"field2<type>">>],
+            raw_bookmark = true
+        },
+    meck:expect(
+        dreyfus_fabric_search,
+        go,
+        [db_name, IdxDDoc, IdxName, QueryArgs],
+        meck:val({ok, [bookmark, 1], undefined, [], undefined, undefined})
+    ),
+    meck:expect(mango_selector_text, convert, [selector], meck:val(query)),
+    meck:expect(mango_selector, match, fun(selector, {doc, _N}) -> true end),
+    ?assertEqual({ok, {acc, 3}}, execute(Cursor, fun foo:add_key_only/2, {acc, 
0})),
+    ?assertEqual(0, meck:num_calls(dreyfus_fabric, get_json_docs, 2)),
+    ?assertEqual(0, meck:num_calls(couch_stats, increment_counter, 1)),
+    ?assertEqual(0, meck:num_calls(mango_execution_stats, incr_docs_examined, 
1)),
+    ?assertEqual(0, meck:num_calls(mango_execution_stats, 
incr_results_returned, 1)).
+
+t_execute_more_results(_) ->
+    Options = [{partition, partition}, {sort, {[]}}, {bookmark, [bookmark, 
0]}],
+    Cursor = #cursor{
+        db = db,
+        index = #idx{ddoc = <<"ddoc">>, name = <<"index">>},
+        limit = 10,
+        skip = 0,
+        fields = fields,
+        selector = selector,
+        opts = Options,
+        execution_stats = stats
+    },
+    meck:expect(
+        dreyfus_fabric_search,
+        go,
+        fun(db_name, <<"ddoc">>, <<"index">>, QueryArgs) ->
+            #index_query_args{
+                q = query,
+                partition = partition,
+                limit = _L,
+                bookmark = [bookmark, B],
+                sort = relevance,
+                raw_bookmark = true
+            } = QueryArgs,
+            Hits =
+                case B of
+                    0 ->
+                        Hit1 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, 1}}]}},
+                        Hit2 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, not_found}}]}},
+                        Hit3 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, not_found}}]}},
+                        [Hit1, Hit2, Hit3];
+                    4 ->
+                        Hit4 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, 4}}]}},
+                        Hit5 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, 5}}]}},
+                        [Hit4, Hit5];
+                    _ ->
+                        []
+                end,
+            {ok, [bookmark, B + 1], undefined, Hits, undefined, undefined}
+        end
+    ),
+    meck:expect(mango_selector_text, convert, [selector], meck:val(query)),
+    meck:expect(mango_selector, match, fun(selector, {doc, _N}) -> true end),
+    ?assertEqual({ok, {acc, 6}}, execute(Cursor, fun foo:normal/2, {acc, 0})),
+    ?assertEqual(3, meck:num_calls(couch_stats, increment_counter, 1)),
+    ?assertEqual(3, meck:num_calls(mango_execution_stats, incr_docs_examined, 
1)),
+    ?assertEqual(3, meck:num_calls(mango_execution_stats, 
incr_results_returned, 1)).
+
+t_execute_unique_results(_) ->
+    Options = [{partition, partition}, {sort, {[]}}, {bookmark, []}],
+    Cursor = #cursor{
+        db = db,
+        index = #idx{ddoc = <<"ddoc">>, name = <<"index">>},
+        limit = 10,
+        skip = 0,
+        fields = fields,
+        selector = selector,
+        opts = Options,
+        execution_stats = stats
+    },
+    meck:expect(
+        dreyfus_fabric_search,
+        go,
+        fun(db_name, <<"ddoc">>, <<"index">>, QueryArgs) ->
+            #index_query_args{
+                q = query,
+                partition = partition,
+                limit = _L,
+                bookmark = B,
+                sort = relevance,
+                raw_bookmark = true
+            } = QueryArgs,
+            {Bookmark, Hits} =
+                case B of
+                    nil ->
+                        Hit1 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, 1}}]}},
+                        Hit2 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, 2}}]}},
+                        Hit3 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, 3}}]}},
+                        {[bookmark, 0], [Hit1, Hit2, Hit3]};
+                    [bookmark, 3] ->
+                        Hit1 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, 1}}]}},
+                        Hit2 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, 2}}]}},
+                        Hit3 = #sortable{item = #hit{fields = [{<<"_id">>, 
{id, 3}}]}},
+                        {[bookmark, 4], [Hit3, Hit2, Hit1]};
+                    [bookmark, N] ->
+                        {[bookmark, N + 1], []}
+                end,
+            {ok, Bookmark, undefined, Hits, undefined, undefined}
+        end
+    ),
+    meck:expect(mango_selector_text, convert, [selector], meck:val(query)),
+    meck:expect(mango_selector, match, fun(selector, {doc, _N}) -> true end),
+    ?assertEqual({ok, {acc, 6}}, execute(Cursor, fun foo:normal/2, {acc, 0})),
+    ?assertEqual(6, meck:num_calls(couch_stats, increment_counter, 1)),
+    ?assertEqual(6, meck:num_calls(mango_execution_stats, incr_docs_examined, 
1)),
+    ?assertEqual(3, meck:num_calls(mango_execution_stats, 
incr_results_returned, 1)).
+
+t_execute_limit_cutoff(_) ->
+    Limit = 2,
+    Skip = 0,
+    IdxName = <<"index">>,
+    Sort = [{<<"field1">>, <<"desc">>}, {<<"field2">>, <<"asc">>}],
+    Options = [{partition, partition}, {sort, {Sort}}, {bookmark, []}],
+    Cursor = #cursor{
+        db = db,
+        index = #idx{ddoc = <<"_design/ddoc">>, name = IdxName},
+        limit = Limit,
+        skip = Skip,
+        fields = fields,
+        selector = selector,
+        opts = Options,
+        execution_stats = stats
+    },
+    QueryArgs =
+        #index_query_args{
+            q = query,
+            partition = partition,
+            limit = Skip + Limit,
+            bookmark = nil,
+            sort = [<<"-field1<type>">>, <<"field2<type>">>],
+            raw_bookmark = true
+        },
+    Hit1 = #sortable{item = #hit{fields = [{<<"_id">>, {id, 1}}]}},
+    Hit2 = #sortable{item = #hit{fields = [{<<"_id">>, {id, 2}}]}},
+    Hit3 = #sortable{item = #hit{fields = [{<<"_id">>, {id, 3}}]}},
+    Hits = [Hit1, Hit2, Hit3],
+    meck:expect(
+        dreyfus_fabric_search,
+        go,
+        [db_name, <<"ddoc">>, IdxName, QueryArgs],
+        meck:val({ok, [bookmark, 1], undefined, Hits, undefined, undefined})
+    ),
+    meck:expect(mango_selector_text, convert, [selector], meck:val(query)),
+    meck:expect(mango_selector, match, fun(selector, {doc, _N}) -> true end),

Review Comment:
   It is only `{doc, _}` that matters.  I left the `N` suffix around because 
that is how it is used at the other mock function definitions.  `_` itself 
constitutes an anonymous variable, so extending it with a name and making it 
`_N` is more about documentation for the reader.
   
   Mind that `{doc, N}` is not valid JSON document here (see my comment above 
on the reasoning) it just a token to check if the document variable was passed 
on properly.  



-- 
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: notifications-unsubscr...@couchdb.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to