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