diff options
author | Paul J. Davis <paul.joseph.davis@gmail.com> | 2019-04-19 13:22:55 -0500 |
---|---|---|
committer | Paul J. Davis <paul.joseph.davis@gmail.com> | 2019-04-19 13:23:11 -0500 |
commit | 65d53f35a90e1c334e583c87e0a16934a752d1af (patch) | |
tree | aabaeae3a56912fc82a7f2ded50196323756c46e | |
parent | e1fdb6ed37b728786bf68c117d98eb3899d29ab1 (diff) | |
download | couchdb-65d53f35a90e1c334e583c87e0a16934a752d1af.tar.gz |
[TMP] Disable current fabric tests
-rw-r--r-- | src/fabric/src/fabric.erl | 100 | ||||
-rw-r--r-- | src/fabric/src/fabric_db_create.erl | 60 | ||||
-rw-r--r-- | src/fabric/src/fabric_db_info.erl | 62 | ||||
-rw-r--r-- | src/fabric/src/fabric_doc_open.erl | 822 | ||||
-rw-r--r-- | src/fabric/src/fabric_doc_open_revs.erl | 932 | ||||
-rw-r--r-- | src/fabric/src/fabric_doc_purge.erl | 692 | ||||
-rw-r--r-- | src/fabric/src/fabric_doc_update.erl | 282 | ||||
-rw-r--r-- | src/fabric/src/fabric_rpc.erl | 38 | ||||
-rw-r--r-- | src/fabric/src/fabric_streams.erl | 158 | ||||
-rw-r--r-- | src/fabric/src/fabric_util.erl | 48 | ||||
-rw-r--r-- | src/fabric/src/fabric_view.erl | 104 | ||||
-rw-r--r-- | src/fabric/src/fabric_view_changes.erl | 96 | ||||
-rw-r--r-- | src/fabric/test/fabric_rpc_purge_tests.erl | 307 |
13 files changed, 1697 insertions, 2004 deletions
diff --git a/src/fabric/src/fabric.erl b/src/fabric/src/fabric.erl index 6d04184e6..a1f74a882 100644 --- a/src/fabric/src/fabric.erl +++ b/src/fabric/src/fabric.erl @@ -658,53 +658,53 @@ set_namespace(NS, #mrargs{extra = Extra} = Args) -> Args#mrargs{extra = [{namespace, NS} | Extra]}. --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -update_doc_test_() -> - { - "Update doc tests", { - setup, fun setup/0, fun teardown/1, - fun(Ctx) -> [ - should_throw_conflict(Ctx) - ] end - } - }. - -should_throw_conflict(Doc) -> - ?_test(begin - ?assertThrow(conflict, update_doc(<<"test-db">>, Doc, [])) - end). - - -setup() -> - Doc = #doc{ - id = <<"test_doc">>, - revs = {3, [<<5,68,252,180,43,161,216,223,26,119,71,219,212,229, - 159,113>>]}, - body = {[{<<"foo">>,<<"asdf">>},{<<"author">>,<<"tom">>}]}, - atts = [], deleted = false, meta = [] - }, - ok = application:ensure_started(config), - ok = meck:expect(mem3, shards, fun(_, _) -> [] end), - ok = meck:expect(mem3, quorum, fun(_) -> 1 end), - ok = meck:expect(rexi, cast, fun(_, _) -> ok end), - ok = meck:expect(rexi_utils, recv, - fun(_, _, _, _, _, _) -> - {ok, {error, [{Doc, conflict}]}} - end), - ok = meck:expect(couch_util, reorder_results, - fun(_, [{_, Res}]) -> - [Res] - end), - ok = meck:expect(fabric_util, create_monitors, fun(_) -> ok end), - ok = meck:expect(rexi_monitor, stop, fun(_) -> ok end), - Doc. - - -teardown(_) -> - meck:unload(), - ok = application:stop(config). - - --endif. +%% -ifdef(TEST). +%% -include_lib("eunit/include/eunit.hrl"). +%% +%% update_doc_test_() -> +%% { +%% "Update doc tests", { +%% setup, fun setup/0, fun teardown/1, +%% fun(Ctx) -> [ +%% should_throw_conflict(Ctx) +%% ] end +%% } +%% }. +%% +%% should_throw_conflict(Doc) -> +%% ?_test(begin +%% ?assertThrow(conflict, update_doc(<<"test-db">>, Doc, [])) +%% end). +%% +%% +%% setup() -> +%% Doc = #doc{ +%% id = <<"test_doc">>, +%% revs = {3, [<<5,68,252,180,43,161,216,223,26,119,71,219,212,229, +%% 159,113>>]}, +%% body = {[{<<"foo">>,<<"asdf">>},{<<"author">>,<<"tom">>}]}, +%% atts = [], deleted = false, meta = [] +%% }, +%% ok = application:ensure_started(config), +%% ok = meck:expect(mem3, shards, fun(_, _) -> [] end), +%% ok = meck:expect(mem3, quorum, fun(_) -> 1 end), +%% ok = meck:expect(rexi, cast, fun(_, _) -> ok end), +%% ok = meck:expect(rexi_utils, recv, +%% fun(_, _, _, _, _, _) -> +%% {ok, {error, [{Doc, conflict}]}} +%% end), +%% ok = meck:expect(couch_util, reorder_results, +%% fun(_, [{_, Res}]) -> +%% [Res] +%% end), +%% ok = meck:expect(fabric_util, create_monitors, fun(_) -> ok end), +%% ok = meck:expect(rexi_monitor, stop, fun(_) -> ok end), +%% Doc. +%% +%% +%% teardown(_) -> +%% meck:unload(), +%% ok = application:stop(config). +%% +%% +%% -endif. diff --git a/src/fabric/src/fabric_db_create.erl b/src/fabric/src/fabric_db_create.erl index 2edc6dc64..81f1ecba1 100644 --- a/src/fabric/src/fabric_db_create.erl +++ b/src/fabric/src/fabric_db_create.erl @@ -185,33 +185,33 @@ make_document([#shard{dbname=DbName}|_] = Shards, Suffix, Options) -> db_exists(DbName) -> is_list(catch mem3:shards(DbName)). --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -db_exists_for_existing_db_test() -> - start_meck_(), - Mock = fun(DbName) when is_binary(DbName) -> - [#shard{dbname = DbName, range = [0,100]}] - end, - ok = meck:expect(mem3, shards, Mock), - ?assertEqual(true, db_exists(<<"foobar">>)), - ?assertEqual(true, meck:validate(mem3)), - stop_meck_(). - -db_exists_for_missing_db_test() -> - start_meck_(), - Mock = fun(DbName) -> - erlang:error(database_does_not_exist, DbName) - end, - ok = meck:expect(mem3, shards, Mock), - ?assertEqual(false, db_exists(<<"foobar">>)), - ?assertEqual(false, meck:validate(mem3)), - stop_meck_(). - -start_meck_() -> - ok = meck:new(mem3). - -stop_meck_() -> - ok = meck:unload(mem3). - --endif. +%% -ifdef(TEST). +%% -include_lib("eunit/include/eunit.hrl"). +%% +%% db_exists_for_existing_db_test() -> +%% start_meck_(), +%% Mock = fun(DbName) when is_binary(DbName) -> +%% [#shard{dbname = DbName, range = [0,100]}] +%% end, +%% ok = meck:expect(mem3, shards, Mock), +%% ?assertEqual(true, db_exists(<<"foobar">>)), +%% ?assertEqual(true, meck:validate(mem3)), +%% stop_meck_(). +%% +%% db_exists_for_missing_db_test() -> +%% start_meck_(), +%% Mock = fun(DbName) -> +%% erlang:error(database_does_not_exist, DbName) +%% end, +%% ok = meck:expect(mem3, shards, Mock), +%% ?assertEqual(false, db_exists(<<"foobar">>)), +%% ?assertEqual(false, meck:validate(mem3)), +%% stop_meck_(). +%% +%% start_meck_() -> +%% ok = meck:new(mem3). +%% +%% stop_meck_() -> +%% ok = meck:unload(mem3). +%% +%% -endif. diff --git a/src/fabric/src/fabric_db_info.erl b/src/fabric/src/fabric_db_info.erl index fe93878b5..e4dd743c2 100644 --- a/src/fabric/src/fabric_db_info.erl +++ b/src/fabric/src/fabric_db_info.erl @@ -156,34 +156,34 @@ get_cluster_info(Shards) -> {ok, [{q, Q}, {n, N}, {w, WR}, {r, WR}]}. --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -get_cluster_info_test_() -> - { - setup, - fun setup/0, - fun get_cluster_info_test_generator/1 - }. - - -setup() -> - Quorums = [1, 2, 3], - Shards = [1, 3, 5, 8, 12, 24], - [{N, Q} || N <- Quorums, Q <- Shards]. - -get_cluster_info_test_generator([]) -> - []; -get_cluster_info_test_generator([{N, Q} | Rest]) -> - {generator, - fun() -> - Nodes = lists:seq(1, 8), - Shards = mem3_util:create_partition_map(<<"foo">>, N, Q, Nodes), - {ok, Info} = get_cluster_info(Shards), - [ - ?_assertEqual(N, couch_util:get_value(n, Info)), - ?_assertEqual(Q, couch_util:get_value(q, Info)) - ] ++ get_cluster_info_test_generator(Rest) - end}. - --endif. +%% -ifdef(TEST). +%% -include_lib("eunit/include/eunit.hrl"). +%% +%% get_cluster_info_test_() -> +%% { +%% setup, +%% fun setup/0, +%% fun get_cluster_info_test_generator/1 +%% }. +%% +%% +%% setup() -> +%% Quorums = [1, 2, 3], +%% Shards = [1, 3, 5, 8, 12, 24], +%% [{N, Q} || N <- Quorums, Q <- Shards]. +%% +%% get_cluster_info_test_generator([]) -> +%% []; +%% get_cluster_info_test_generator([{N, Q} | Rest]) -> +%% {generator, +%% fun() -> +%% Nodes = lists:seq(1, 8), +%% Shards = mem3_util:create_partition_map(<<"foo">>, N, Q, Nodes), +%% {ok, Info} = get_cluster_info(Shards), +%% [ +%% ?_assertEqual(N, couch_util:get_value(n, Info)), +%% ?_assertEqual(Q, couch_util:get_value(q, Info)) +%% ] ++ get_cluster_info_test_generator(Rest) +%% end}. +%% +%% -endif. diff --git a/src/fabric/src/fabric_doc_open.erl b/src/fabric/src/fabric_doc_open.erl index aafdcfb79..b953cc70e 100644 --- a/src/fabric/src/fabric_doc_open.erl +++ b/src/fabric/src/fabric_doc_open.erl @@ -182,414 +182,414 @@ format_reply(Else, _) -> Else. --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - - -setup() -> - meck:new([ - couch_log, - couch_stats, - fabric, - fabric_util, - mem3, - rexi, - rexi_monitor - ], [passthrough]). - - -teardown(_) -> - meck:unload(). - - -open_doc_test_() -> - { - foreach, - fun setup/0, - fun teardown/1, - [ - t_is_r_met(), - t_handle_message_down(), - t_handle_message_exit(), - t_handle_message_reply(), - t_store_node_revs(), - t_read_repair(), - t_handle_response_quorum_met(), - t_get_doc_info() - ] - }. - - -t_is_r_met() -> - ?_test(begin - Workers0 = [], - Workers1 = [nil], - Workers2 = [nil, nil], - - SuccessCases = [ - {{true, foo}, [fabric_util:kv(foo, 2)], 2}, - {{true, foo}, [fabric_util:kv(foo, 3)], 2}, - {{true, foo}, [fabric_util:kv(foo, 1)], 1}, - {{true, foo}, [fabric_util:kv(foo, 2), fabric_util:kv(bar, 1)], 2}, - {{true, bar}, [fabric_util:kv(bar, 1), fabric_util:kv(bar, 2)], 2}, - {{true, bar}, [fabric_util:kv(bar, 2), fabric_util:kv(foo, 1)], 2} - ], - lists:foreach(fun({Expect, Replies, Q}) -> - ?assertEqual(Expect, is_r_met(Workers0, Replies, Q)) - end, SuccessCases), - - WaitForMoreCases = [ - {[fabric_util:kv(foo, 1)], 2}, - {[fabric_util:kv(foo, 2)], 3}, - {[fabric_util:kv(foo, 1), fabric_util:kv(bar, 1)], 2} - ], - lists:foreach(fun({Replies, Q}) -> - ?assertEqual(wait_for_more, is_r_met(Workers2, Replies, Q)) - end, WaitForMoreCases), - - FailureCases = [ - {Workers0, [fabric_util:kv(foo, 1)], 2}, - {Workers1, [fabric_util:kv(foo, 1)], 2}, - {Workers1, [fabric_util:kv(foo, 1), fabric_util:kv(bar, 1)], 2}, - {Workers1, [fabric_util:kv(foo, 2)], 3} - ], - lists:foreach(fun({Workers, Replies, Q}) -> - ?assertEqual(no_more_workers, is_r_met(Workers, Replies, Q)) - end, FailureCases) - end). - - -t_handle_message_down() -> - Node0 = 'foo@localhost', - Node1 = 'bar@localhost', - Down0 = {rexi_DOWN, nil, {nil, Node0}, nil}, - Down1 = {rexi_DOWN, nil, {nil, Node1}, nil}, - Workers0 = [#shard{node=Node0} || _ <- [a, b]], - Worker1 = #shard{node=Node1}, - Workers1 = Workers0 ++ [Worker1], - - ?_test(begin - % Stop when no more workers are left - ?assertEqual( - {stop, #acc{workers=[]}}, - handle_message(Down0, nil, #acc{workers=Workers0}) - ), - - % Continue when we have more workers - ?assertEqual( - {ok, #acc{workers=[Worker1]}}, - handle_message(Down0, nil, #acc{workers=Workers1}) - ), - - % A second DOWN removes the remaining workers - ?assertEqual( - {stop, #acc{workers=[]}}, - handle_message(Down1, nil, #acc{workers=[Worker1]}) - ) - end). - - -t_handle_message_exit() -> - Exit = {rexi_EXIT, nil}, - Worker0 = #shard{ref=erlang:make_ref()}, - Worker1 = #shard{ref=erlang:make_ref()}, - - ?_test(begin - % Only removes the specified worker - ?assertEqual( - {ok, #acc{workers=[Worker1]}}, - handle_message(Exit, Worker0, #acc{workers=[Worker0, Worker1]}) - ), - - ?assertEqual( - {ok, #acc{workers=[Worker0]}}, - handle_message(Exit, Worker1, #acc{workers=[Worker0, Worker1]}) - ), - - % We bail if it was the last worker - ?assertEqual( - {stop, #acc{workers=[]}}, - handle_message(Exit, Worker0, #acc{workers=[Worker0]}) - ) - end). - - -t_handle_message_reply() -> - Worker0 = #shard{ref=erlang:make_ref()}, - Worker1 = #shard{ref=erlang:make_ref()}, - Worker2 = #shard{ref=erlang:make_ref()}, - Workers = [Worker0, Worker1, Worker2], - Acc0 = #acc{workers=Workers, r=2, replies=[]}, - - ?_test(begin - meck:expect(rexi, kill, fun(_, _) -> ok end), - - % Test that we continue when we haven't met R yet - ?assertMatch( - {ok, #acc{ - workers=[Worker0, Worker1], - replies=[{foo, {foo, 1}}] - }}, - handle_message(foo, Worker2, Acc0) - ), - - ?assertMatch( - {ok, #acc{ - workers=[Worker0, Worker1], - replies=[{bar, {bar, 1}}, {foo, {foo, 1}}] - }}, - handle_message(bar, Worker2, Acc0#acc{ - replies=[{foo, {foo, 1}}] - }) - ), - - % Test that we don't get a quorum when R isn't met. q_reply - % isn't set and state remains unchanged and {stop, NewAcc} - % is returned. Bit subtle on the assertions here. - - ?assertMatch( - {stop, #acc{workers=[], replies=[{foo, {foo, 1}}]}}, - handle_message(foo, Worker0, Acc0#acc{workers=[Worker0]}) - ), - - ?assertMatch( - {stop, #acc{ - workers=[], - replies=[{bar, {bar, 1}}, {foo, {foo, 1}}] - }}, - handle_message(bar, Worker0, Acc0#acc{ - workers=[Worker0], - replies=[{foo, {foo, 1}}] - }) - ), - - % Check that when R is met we stop with a new state and - % a q_reply. - - ?assertMatch( - {stop, #acc{ - workers=[], - replies=[{foo, {foo, 2}}], - state=r_met, - q_reply=foo - }}, - handle_message(foo, Worker1, Acc0#acc{ - workers=[Worker0, Worker1], - replies=[{foo, {foo, 1}}] - }) - ), - - ?assertEqual( - {stop, #acc{ - workers=[], - r=1, - replies=[{foo, {foo, 1}}], - state=r_met, - q_reply=foo - }}, - handle_message(foo, Worker0, Acc0#acc{r=1}) - ), - - ?assertMatch( - {stop, #acc{ - workers=[], - replies=[{bar, {bar, 1}}, {foo, {foo, 2}}], - state=r_met, - q_reply=foo - }}, - handle_message(foo, Worker0, Acc0#acc{ - workers=[Worker0], - replies=[{bar, {bar, 1}}, {foo, {foo, 1}}] - }) - ) - end). - - -t_store_node_revs() -> - W1 = #shard{node = w1, ref = erlang:make_ref()}, - W2 = #shard{node = w2, ref = erlang:make_ref()}, - W3 = #shard{node = w3, ref = erlang:make_ref()}, - Foo1 = {ok, #doc{id = <<"bar">>, revs = {1, [<<"foo">>]}}}, - Foo2 = {ok, #doc{id = <<"bar">>, revs = {2, [<<"foo2">>, <<"foo">>]}}}, - NFM = {not_found, missing}, - - InitAcc = #acc{workers = [W1, W2, W3], replies = [], r = 2}, - - ?_test(begin - meck:expect(rexi, kill, fun(_, _) -> ok end), - - % Simple case - {ok, #acc{node_revs = NodeRevs1}} = handle_message(Foo1, W1, InitAcc), - ?assertEqual([{w1, [{1, <<"foo">>}]}], NodeRevs1), - - % Make sure we only hold the head rev - {ok, #acc{node_revs = NodeRevs2}} = handle_message(Foo2, W1, InitAcc), - ?assertEqual([{w1, [{2, <<"foo2">>}]}], NodeRevs2), - - % Make sure we don't capture anything on error - {ok, #acc{node_revs = NodeRevs3}} = handle_message(NFM, W1, InitAcc), - ?assertEqual([], NodeRevs3), - - % Make sure we accumulate node revs - Acc1 = InitAcc#acc{node_revs = [{w1, [{1, <<"foo">>}]}]}, - {ok, #acc{node_revs = NodeRevs4}} = handle_message(Foo2, W2, Acc1), - ?assertEqual( - [{w2, [{2, <<"foo2">>}]}, {w1, [{1, <<"foo">>}]}], - NodeRevs4 - ), - - % Make sure rexi_DOWN doesn't modify node_revs - Down = {rexi_DOWN, nil, {nil, w1}, nil}, - {ok, #acc{node_revs = NodeRevs5}} = handle_message(Down, W2, Acc1), - ?assertEqual([{w1, [{1, <<"foo">>}]}], NodeRevs5), - - % Make sure rexi_EXIT doesn't modify node_revs - Exit = {rexi_EXIT, reason}, - {ok, #acc{node_revs = NodeRevs6}} = handle_message(Exit, W2, Acc1), - ?assertEqual([{w1, [{1, <<"foo">>}]}], NodeRevs6), - - % Make sure an error doesn't remove any node revs - {ok, #acc{node_revs = NodeRevs7}} = handle_message(NFM, W2, Acc1), - ?assertEqual([{w1, [{1, <<"foo">>}]}], NodeRevs7), - - % Make sure we have all of our node_revs when meeting - % quorum - {ok, Acc2} = handle_message(Foo1, W1, InitAcc), - {ok, Acc3} = handle_message(Foo2, W2, Acc2), - {stop, Acc4} = handle_message(NFM, W3, Acc3), - ?assertEqual( - [{w2, [{2, <<"foo2">>}]}, {w1, [{1, <<"foo">>}]}], - Acc4#acc.node_revs - ) - end). - - -t_read_repair() -> - Foo1 = {ok, #doc{revs = {1,[<<"foo">>]}}}, - Foo2 = {ok, #doc{revs = {2,[<<"foo2">>,<<"foo">>]}}}, - NFM = {not_found, missing}, - - ?_test(begin - meck:expect(couch_log, notice, fun(_, _) -> ok end), - meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - - % Test when we have actual doc data to repair - meck:expect(fabric, update_docs, fun(_, [_], _) -> {ok, []} end), - Acc0 = #acc{ - dbname = <<"name">>, - replies = [fabric_util:kv(Foo1,1)] - }, - ?assertEqual(Foo1, read_repair(Acc0)), - - meck:expect(fabric, update_docs, fun(_, [_, _], _) -> {ok, []} end), - Acc1 = #acc{ - dbname = <<"name">>, - replies = [fabric_util:kv(Foo1,1), fabric_util:kv(Foo2,1)] - }, - ?assertEqual(Foo2, read_repair(Acc1)), - - % Test when we have nothing but errors - Acc2 = #acc{replies=[fabric_util:kv(NFM, 1)]}, - ?assertEqual(NFM, read_repair(Acc2)), - - Acc3 = #acc{replies=[fabric_util:kv(NFM,1), fabric_util:kv(foo,2)]}, - ?assertEqual(NFM, read_repair(Acc3)), - - Acc4 = #acc{replies=[fabric_util:kv(foo,1), fabric_util:kv(bar,1)]}, - ?assertEqual(bar, read_repair(Acc4)) - end). - - -t_handle_response_quorum_met() -> - Foo1 = {ok, #doc{revs = {1,[<<"foo">>]}}}, - Foo2 = {ok, #doc{revs = {2,[<<"foo2">>,<<"foo">>]}}}, - Bar1 = {ok, #doc{revs = {1,[<<"bar">>]}}}, - - ?_test(begin - meck:expect(couch_log, notice, fun(_, _) -> ok end), - meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, []} end), - meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - - BasicOkAcc = #acc{ - state=r_met, - replies=[fabric_util:kv(Foo1,2)], - q_reply=Foo1 - }, - ?assertEqual(Foo1, handle_response(BasicOkAcc)), - - WithAncestorsAcc = #acc{ - state=r_met, - replies=[fabric_util:kv(Foo1,1), fabric_util:kv(Foo2,2)], - q_reply=Foo2 - }, - ?assertEqual(Foo2, handle_response(WithAncestorsAcc)), - - % This also checks when the quorum isn't the most recent - % revision. - DeeperWinsAcc = #acc{ - state=r_met, - replies=[fabric_util:kv(Foo1,2), fabric_util:kv(Foo2,1)], - q_reply=Foo1 - }, - ?assertEqual(Foo2, handle_response(DeeperWinsAcc)), - - % Check that we return the proper doc based on rev - % (ie, pos is equal) - BiggerRevWinsAcc = #acc{ - state=r_met, - replies=[fabric_util:kv(Foo1,1), fabric_util:kv(Bar1,2)], - q_reply=Bar1 - }, - ?assertEqual(Foo1, handle_response(BiggerRevWinsAcc)) - - % r_not_met is a proxy to read_repair so we rely on - % read_repair_test for those conditions. - end). - - -t_get_doc_info() -> - ?_test(begin - meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, []} end), - meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - meck:expect(fabric_util, submit_jobs, fun(_, _, _) -> ok end), - meck:expect(fabric_util, create_monitors, fun(_) -> ok end), - meck:expect(rexi_monitor, stop, fun(_) -> ok end), - meck:expect(mem3, shards, fun(_, _) -> ok end), - meck:expect(mem3, n, fun(_) -> 3 end), - meck:expect(mem3, quorum, fun(_) -> 2 end), - - meck:expect(fabric_util, recv, fun(_, _, _, _) -> - {ok, #acc{state = r_not_met}} - end), - Rsp1 = fabric_doc_open:go("test", "one", [doc_info]), - ?assertEqual({error, quorum_not_met}, Rsp1), - - Rsp2 = fabric_doc_open:go("test", "one", [{doc_info, full}]), - ?assertEqual({error, quorum_not_met}, Rsp2), - - meck:expect(fabric_util, recv, fun(_, _, _, _) -> - {ok, #acc{state = r_met, q_reply = not_found}} - end), - MissingRsp1 = fabric_doc_open:go("test", "one", [doc_info]), - ?assertEqual({not_found, missing}, MissingRsp1), - MissingRsp2 = fabric_doc_open:go("test", "one", [{doc_info, full}]), - ?assertEqual({not_found, missing}, MissingRsp2), - - meck:expect(fabric_util, recv, fun(_, _, _, _) -> - A = #doc_info{}, - {ok, #acc{state = r_met, q_reply = {ok, A}}} - end), - {ok, Rec1} = fabric_doc_open:go("test", "one", [doc_info]), - ?assert(is_record(Rec1, doc_info)), - - meck:expect(fabric_util, recv, fun(_, _, _, _) -> - A = #full_doc_info{deleted = true}, - {ok, #acc{state = r_met, q_reply = {ok, A}}} - end), - Rsp3 = fabric_doc_open:go("test", "one", [{doc_info, full}]), - ?assertEqual({not_found, deleted}, Rsp3), - {ok, Rec2} = fabric_doc_open:go("test", "one", [{doc_info, full},deleted]), - ?assert(is_record(Rec2, full_doc_info)) - end). - --endif. +%% -ifdef(TEST). +%% -include_lib("eunit/include/eunit.hrl"). +%% +%% +%% setup() -> +%% meck:new([ +%% couch_log, +%% couch_stats, +%% fabric, +%% fabric_util, +%% mem3, +%% rexi, +%% rexi_monitor +%% ], [passthrough]). +%% +%% +%% teardown(_) -> +%% meck:unload(). +%% +%% +%% open_doc_test_() -> +%% { +%% foreach, +%% fun setup/0, +%% fun teardown/1, +%% [ +%% t_is_r_met(), +%% t_handle_message_down(), +%% t_handle_message_exit(), +%% t_handle_message_reply(), +%% t_store_node_revs(), +%% t_read_repair(), +%% t_handle_response_quorum_met(), +%% t_get_doc_info() +%% ] +%% }. +%% +%% +%% t_is_r_met() -> +%% ?_test(begin +%% Workers0 = [], +%% Workers1 = [nil], +%% Workers2 = [nil, nil], +%% +%% SuccessCases = [ +%% {{true, foo}, [fabric_util:kv(foo, 2)], 2}, +%% {{true, foo}, [fabric_util:kv(foo, 3)], 2}, +%% {{true, foo}, [fabric_util:kv(foo, 1)], 1}, +%% {{true, foo}, [fabric_util:kv(foo, 2), fabric_util:kv(bar, 1)], 2}, +%% {{true, bar}, [fabric_util:kv(bar, 1), fabric_util:kv(bar, 2)], 2}, +%% {{true, bar}, [fabric_util:kv(bar, 2), fabric_util:kv(foo, 1)], 2} +%% ], +%% lists:foreach(fun({Expect, Replies, Q}) -> +%% ?assertEqual(Expect, is_r_met(Workers0, Replies, Q)) +%% end, SuccessCases), +%% +%% WaitForMoreCases = [ +%% {[fabric_util:kv(foo, 1)], 2}, +%% {[fabric_util:kv(foo, 2)], 3}, +%% {[fabric_util:kv(foo, 1), fabric_util:kv(bar, 1)], 2} +%% ], +%% lists:foreach(fun({Replies, Q}) -> +%% ?assertEqual(wait_for_more, is_r_met(Workers2, Replies, Q)) +%% end, WaitForMoreCases), +%% +%% FailureCases = [ +%% {Workers0, [fabric_util:kv(foo, 1)], 2}, +%% {Workers1, [fabric_util:kv(foo, 1)], 2}, +%% {Workers1, [fabric_util:kv(foo, 1), fabric_util:kv(bar, 1)], 2}, +%% {Workers1, [fabric_util:kv(foo, 2)], 3} +%% ], +%% lists:foreach(fun({Workers, Replies, Q}) -> +%% ?assertEqual(no_more_workers, is_r_met(Workers, Replies, Q)) +%% end, FailureCases) +%% end). +%% +%% +%% t_handle_message_down() -> +%% Node0 = 'foo@localhost', +%% Node1 = 'bar@localhost', +%% Down0 = {rexi_DOWN, nil, {nil, Node0}, nil}, +%% Down1 = {rexi_DOWN, nil, {nil, Node1}, nil}, +%% Workers0 = [#shard{node=Node0} || _ <- [a, b]], +%% Worker1 = #shard{node=Node1}, +%% Workers1 = Workers0 ++ [Worker1], +%% +%% ?_test(begin +%% % Stop when no more workers are left +%% ?assertEqual( +%% {stop, #acc{workers=[]}}, +%% handle_message(Down0, nil, #acc{workers=Workers0}) +%% ), +%% +%% % Continue when we have more workers +%% ?assertEqual( +%% {ok, #acc{workers=[Worker1]}}, +%% handle_message(Down0, nil, #acc{workers=Workers1}) +%% ), +%% +%% % A second DOWN removes the remaining workers +%% ?assertEqual( +%% {stop, #acc{workers=[]}}, +%% handle_message(Down1, nil, #acc{workers=[Worker1]}) +%% ) +%% end). +%% +%% +%% t_handle_message_exit() -> +%% Exit = {rexi_EXIT, nil}, +%% Worker0 = #shard{ref=erlang:make_ref()}, +%% Worker1 = #shard{ref=erlang:make_ref()}, +%% +%% ?_test(begin +%% % Only removes the specified worker +%% ?assertEqual( +%% {ok, #acc{workers=[Worker1]}}, +%% handle_message(Exit, Worker0, #acc{workers=[Worker0, Worker1]}) +%% ), +%% +%% ?assertEqual( +%% {ok, #acc{workers=[Worker0]}}, +%% handle_message(Exit, Worker1, #acc{workers=[Worker0, Worker1]}) +%% ), +%% +%% % We bail if it was the last worker +%% ?assertEqual( +%% {stop, #acc{workers=[]}}, +%% handle_message(Exit, Worker0, #acc{workers=[Worker0]}) +%% ) +%% end). +%% +%% +%% t_handle_message_reply() -> +%% Worker0 = #shard{ref=erlang:make_ref()}, +%% Worker1 = #shard{ref=erlang:make_ref()}, +%% Worker2 = #shard{ref=erlang:make_ref()}, +%% Workers = [Worker0, Worker1, Worker2], +%% Acc0 = #acc{workers=Workers, r=2, replies=[]}, +%% +%% ?_test(begin +%% meck:expect(rexi, kill, fun(_, _) -> ok end), +%% +%% % Test that we continue when we haven't met R yet +%% ?assertMatch( +%% {ok, #acc{ +%% workers=[Worker0, Worker1], +%% replies=[{foo, {foo, 1}}] +%% }}, +%% handle_message(foo, Worker2, Acc0) +%% ), +%% +%% ?assertMatch( +%% {ok, #acc{ +%% workers=[Worker0, Worker1], +%% replies=[{bar, {bar, 1}}, {foo, {foo, 1}}] +%% }}, +%% handle_message(bar, Worker2, Acc0#acc{ +%% replies=[{foo, {foo, 1}}] +%% }) +%% ), +%% +%% % Test that we don't get a quorum when R isn't met. q_reply +%% % isn't set and state remains unchanged and {stop, NewAcc} +%% % is returned. Bit subtle on the assertions here. +%% +%% ?assertMatch( +%% {stop, #acc{workers=[], replies=[{foo, {foo, 1}}]}}, +%% handle_message(foo, Worker0, Acc0#acc{workers=[Worker0]}) +%% ), +%% +%% ?assertMatch( +%% {stop, #acc{ +%% workers=[], +%% replies=[{bar, {bar, 1}}, {foo, {foo, 1}}] +%% }}, +%% handle_message(bar, Worker0, Acc0#acc{ +%% workers=[Worker0], +%% replies=[{foo, {foo, 1}}] +%% }) +%% ), +%% +%% % Check that when R is met we stop with a new state and +%% % a q_reply. +%% +%% ?assertMatch( +%% {stop, #acc{ +%% workers=[], +%% replies=[{foo, {foo, 2}}], +%% state=r_met, +%% q_reply=foo +%% }}, +%% handle_message(foo, Worker1, Acc0#acc{ +%% workers=[Worker0, Worker1], +%% replies=[{foo, {foo, 1}}] +%% }) +%% ), +%% +%% ?assertEqual( +%% {stop, #acc{ +%% workers=[], +%% r=1, +%% replies=[{foo, {foo, 1}}], +%% state=r_met, +%% q_reply=foo +%% }}, +%% handle_message(foo, Worker0, Acc0#acc{r=1}) +%% ), +%% +%% ?assertMatch( +%% {stop, #acc{ +%% workers=[], +%% replies=[{bar, {bar, 1}}, {foo, {foo, 2}}], +%% state=r_met, +%% q_reply=foo +%% }}, +%% handle_message(foo, Worker0, Acc0#acc{ +%% workers=[Worker0], +%% replies=[{bar, {bar, 1}}, {foo, {foo, 1}}] +%% }) +%% ) +%% end). +%% +%% +%% t_store_node_revs() -> +%% W1 = #shard{node = w1, ref = erlang:make_ref()}, +%% W2 = #shard{node = w2, ref = erlang:make_ref()}, +%% W3 = #shard{node = w3, ref = erlang:make_ref()}, +%% Foo1 = {ok, #doc{id = <<"bar">>, revs = {1, [<<"foo">>]}}}, +%% Foo2 = {ok, #doc{id = <<"bar">>, revs = {2, [<<"foo2">>, <<"foo">>]}}}, +%% NFM = {not_found, missing}, +%% +%% InitAcc = #acc{workers = [W1, W2, W3], replies = [], r = 2}, +%% +%% ?_test(begin +%% meck:expect(rexi, kill, fun(_, _) -> ok end), +%% +%% % Simple case +%% {ok, #acc{node_revs = NodeRevs1}} = handle_message(Foo1, W1, InitAcc), +%% ?assertEqual([{w1, [{1, <<"foo">>}]}], NodeRevs1), +%% +%% % Make sure we only hold the head rev +%% {ok, #acc{node_revs = NodeRevs2}} = handle_message(Foo2, W1, InitAcc), +%% ?assertEqual([{w1, [{2, <<"foo2">>}]}], NodeRevs2), +%% +%% % Make sure we don't capture anything on error +%% {ok, #acc{node_revs = NodeRevs3}} = handle_message(NFM, W1, InitAcc), +%% ?assertEqual([], NodeRevs3), +%% +%% % Make sure we accumulate node revs +%% Acc1 = InitAcc#acc{node_revs = [{w1, [{1, <<"foo">>}]}]}, +%% {ok, #acc{node_revs = NodeRevs4}} = handle_message(Foo2, W2, Acc1), +%% ?assertEqual( +%% [{w2, [{2, <<"foo2">>}]}, {w1, [{1, <<"foo">>}]}], +%% NodeRevs4 +%% ), +%% +%% % Make sure rexi_DOWN doesn't modify node_revs +%% Down = {rexi_DOWN, nil, {nil, w1}, nil}, +%% {ok, #acc{node_revs = NodeRevs5}} = handle_message(Down, W2, Acc1), +%% ?assertEqual([{w1, [{1, <<"foo">>}]}], NodeRevs5), +%% +%% % Make sure rexi_EXIT doesn't modify node_revs +%% Exit = {rexi_EXIT, reason}, +%% {ok, #acc{node_revs = NodeRevs6}} = handle_message(Exit, W2, Acc1), +%% ?assertEqual([{w1, [{1, <<"foo">>}]}], NodeRevs6), +%% +%% % Make sure an error doesn't remove any node revs +%% {ok, #acc{node_revs = NodeRevs7}} = handle_message(NFM, W2, Acc1), +%% ?assertEqual([{w1, [{1, <<"foo">>}]}], NodeRevs7), +%% +%% % Make sure we have all of our node_revs when meeting +%% % quorum +%% {ok, Acc2} = handle_message(Foo1, W1, InitAcc), +%% {ok, Acc3} = handle_message(Foo2, W2, Acc2), +%% {stop, Acc4} = handle_message(NFM, W3, Acc3), +%% ?assertEqual( +%% [{w2, [{2, <<"foo2">>}]}, {w1, [{1, <<"foo">>}]}], +%% Acc4#acc.node_revs +%% ) +%% end). +%% +%% +%% t_read_repair() -> +%% Foo1 = {ok, #doc{revs = {1,[<<"foo">>]}}}, +%% Foo2 = {ok, #doc{revs = {2,[<<"foo2">>,<<"foo">>]}}}, +%% NFM = {not_found, missing}, +%% +%% ?_test(begin +%% meck:expect(couch_log, notice, fun(_, _) -> ok end), +%% meck:expect(couch_stats, increment_counter, fun(_) -> ok end), +%% +%% % Test when we have actual doc data to repair +%% meck:expect(fabric, update_docs, fun(_, [_], _) -> {ok, []} end), +%% Acc0 = #acc{ +%% dbname = <<"name">>, +%% replies = [fabric_util:kv(Foo1,1)] +%% }, +%% ?assertEqual(Foo1, read_repair(Acc0)), +%% +%% meck:expect(fabric, update_docs, fun(_, [_, _], _) -> {ok, []} end), +%% Acc1 = #acc{ +%% dbname = <<"name">>, +%% replies = [fabric_util:kv(Foo1,1), fabric_util:kv(Foo2,1)] +%% }, +%% ?assertEqual(Foo2, read_repair(Acc1)), +%% +%% % Test when we have nothing but errors +%% Acc2 = #acc{replies=[fabric_util:kv(NFM, 1)]}, +%% ?assertEqual(NFM, read_repair(Acc2)), +%% +%% Acc3 = #acc{replies=[fabric_util:kv(NFM,1), fabric_util:kv(foo,2)]}, +%% ?assertEqual(NFM, read_repair(Acc3)), +%% +%% Acc4 = #acc{replies=[fabric_util:kv(foo,1), fabric_util:kv(bar,1)]}, +%% ?assertEqual(bar, read_repair(Acc4)) +%% end). +%% +%% +%% t_handle_response_quorum_met() -> +%% Foo1 = {ok, #doc{revs = {1,[<<"foo">>]}}}, +%% Foo2 = {ok, #doc{revs = {2,[<<"foo2">>,<<"foo">>]}}}, +%% Bar1 = {ok, #doc{revs = {1,[<<"bar">>]}}}, +%% +%% ?_test(begin +%% meck:expect(couch_log, notice, fun(_, _) -> ok end), +%% meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, []} end), +%% meck:expect(couch_stats, increment_counter, fun(_) -> ok end), +%% +%% BasicOkAcc = #acc{ +%% state=r_met, +%% replies=[fabric_util:kv(Foo1,2)], +%% q_reply=Foo1 +%% }, +%% ?assertEqual(Foo1, handle_response(BasicOkAcc)), +%% +%% WithAncestorsAcc = #acc{ +%% state=r_met, +%% replies=[fabric_util:kv(Foo1,1), fabric_util:kv(Foo2,2)], +%% q_reply=Foo2 +%% }, +%% ?assertEqual(Foo2, handle_response(WithAncestorsAcc)), +%% +%% % This also checks when the quorum isn't the most recent +%% % revision. +%% DeeperWinsAcc = #acc{ +%% state=r_met, +%% replies=[fabric_util:kv(Foo1,2), fabric_util:kv(Foo2,1)], +%% q_reply=Foo1 +%% }, +%% ?assertEqual(Foo2, handle_response(DeeperWinsAcc)), +%% +%% % Check that we return the proper doc based on rev +%% % (ie, pos is equal) +%% BiggerRevWinsAcc = #acc{ +%% state=r_met, +%% replies=[fabric_util:kv(Foo1,1), fabric_util:kv(Bar1,2)], +%% q_reply=Bar1 +%% }, +%% ?assertEqual(Foo1, handle_response(BiggerRevWinsAcc)) +%% +%% % r_not_met is a proxy to read_repair so we rely on +%% % read_repair_test for those conditions. +%% end). +%% +%% +%% t_get_doc_info() -> +%% ?_test(begin +%% meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, []} end), +%% meck:expect(couch_stats, increment_counter, fun(_) -> ok end), +%% meck:expect(fabric_util, submit_jobs, fun(_, _, _) -> ok end), +%% meck:expect(fabric_util, create_monitors, fun(_) -> ok end), +%% meck:expect(rexi_monitor, stop, fun(_) -> ok end), +%% meck:expect(mem3, shards, fun(_, _) -> ok end), +%% meck:expect(mem3, n, fun(_) -> 3 end), +%% meck:expect(mem3, quorum, fun(_) -> 2 end), +%% +%% meck:expect(fabric_util, recv, fun(_, _, _, _) -> +%% {ok, #acc{state = r_not_met}} +%% end), +%% Rsp1 = fabric_doc_open:go("test", "one", [doc_info]), +%% ?assertEqual({error, quorum_not_met}, Rsp1), +%% +%% Rsp2 = fabric_doc_open:go("test", "one", [{doc_info, full}]), +%% ?assertEqual({error, quorum_not_met}, Rsp2), +%% +%% meck:expect(fabric_util, recv, fun(_, _, _, _) -> +%% {ok, #acc{state = r_met, q_reply = not_found}} +%% end), +%% MissingRsp1 = fabric_doc_open:go("test", "one", [doc_info]), +%% ?assertEqual({not_found, missing}, MissingRsp1), +%% MissingRsp2 = fabric_doc_open:go("test", "one", [{doc_info, full}]), +%% ?assertEqual({not_found, missing}, MissingRsp2), +%% +%% meck:expect(fabric_util, recv, fun(_, _, _, _) -> +%% A = #doc_info{}, +%% {ok, #acc{state = r_met, q_reply = {ok, A}}} +%% end), +%% {ok, Rec1} = fabric_doc_open:go("test", "one", [doc_info]), +%% ?assert(is_record(Rec1, doc_info)), +%% +%% meck:expect(fabric_util, recv, fun(_, _, _, _) -> +%% A = #full_doc_info{deleted = true}, +%% {ok, #acc{state = r_met, q_reply = {ok, A}}} +%% end), +%% Rsp3 = fabric_doc_open:go("test", "one", [{doc_info, full}]), +%% ?assertEqual({not_found, deleted}, Rsp3), +%% {ok, Rec2} = fabric_doc_open:go("test", "one", [{doc_info, full},deleted]), +%% ?assert(is_record(Rec2, full_doc_info)) +%% end). +%% +%% -endif. diff --git a/src/fabric/src/fabric_doc_open_revs.erl b/src/fabric/src/fabric_doc_open_revs.erl index 8ac3f30dc..f5b63800e 100644 --- a/src/fabric/src/fabric_doc_open_revs.erl +++ b/src/fabric/src/fabric_doc_open_revs.erl @@ -313,469 +313,469 @@ collapse_duplicate_revs_int([Reply | Rest]) -> [Reply | collapse_duplicate_revs(Rest)]. --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - - -setup() -> - config:start_link([]), - meck:new([fabric, couch_stats, couch_log]), - meck:new(fabric_util, [passthrough]), - meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, nil} end), - meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - meck:expect(couch_log, notice, fun(_, _) -> ok end), - meck:expect(fabric_util, cleanup, fun(_) -> ok end). - - - -teardown(_) -> - (catch meck:unload([fabric, couch_stats, couch_log, fabric_util])), - config:stop(). - - -state0(Revs, Latest) -> - #state{ - worker_count = 3, - workers = - [#shard{node='node1'}, #shard{node='node2'}, #shard{node='node3'}], - r = 2, - revs = Revs, - latest = Latest - }. - - -revs() -> [{1,<<"foo">>}, {1,<<"bar">>}, {1,<<"baz">>}]. - - -foo1() -> {ok, #doc{revs = {1, [<<"foo">>]}}}. -foo2() -> {ok, #doc{revs = {2, [<<"foo2">>, <<"foo">>]}}}. -foo2stemmed() -> {ok, #doc{revs = {2, [<<"foo2">>]}}}. -fooNF() -> {{not_found, missing}, {1,<<"foo">>}}. -foo2NF() -> {{not_found, missing}, {2, <<"foo2">>}}. -bar1() -> {ok, #doc{revs = {1, [<<"bar">>]}}}. -barNF() -> {{not_found, missing}, {1,<<"bar">>}}. -bazNF() -> {{not_found, missing}, {1,<<"baz">>}}. -baz1() -> {ok, #doc{revs = {1, [<<"baz">>]}}}. - - - -open_doc_revs_test_() -> - { - foreach, - fun setup/0, - fun teardown/1, - [ - check_empty_response_not_quorum(), - check_basic_response(), - check_finish_quorum(), - check_finish_quorum_newer(), - check_no_quorum_on_second(), - check_done_on_third(), - check_specific_revs_first_msg(), - check_revs_done_on_agreement(), - check_latest_true(), - check_ancestor_counted_in_quorum(), - check_not_found_counts_for_descendant(), - check_worker_error_skipped(), - check_quorum_only_counts_valid_responses(), - check_empty_list_when_no_workers_reply(), - check_node_rev_stored(), - check_node_rev_store_head_only(), - check_node_rev_store_multiple(), - check_node_rev_dont_store_errors(), - check_node_rev_store_non_errors(), - check_node_rev_store_concatenate(), - check_node_rev_store_concantenate_multiple(), - check_node_rev_unmodified_on_down_or_exit(), - check_not_found_replies_are_removed_when_doc_found(), - check_not_found_returned_when_one_of_docs_not_found(), - check_not_found_returned_when_doc_not_found(), - check_longer_rev_list_returned(), - check_longer_rev_list_not_combined(), - check_not_found_removed_and_longer_rev_list() - ] - }. - - -% Tests for revs=all - - -check_empty_response_not_quorum() -> - % Simple smoke test that we don't think we're - % done with a first empty response - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - W3 = #shard{node='node3'}, - ?_assertMatch( - {ok, #state{workers = [W2, W3]}}, - handle_message({ok, []}, W1, state0(all, false)) - ). - - -check_basic_response() -> - % Check that we've handle a response - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - W3 = #shard{node='node3'}, - ?_assertMatch( - {ok, #state{reply_count = 1, workers = [W2, W3]}}, - handle_message({ok, [foo1(), bar1()]}, W1, state0(all, false)) - ). - - -check_finish_quorum() -> - % Two messages with the same revisions means we're done - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - S0 = state0(all, false), - {ok, S1} = handle_message({ok, [foo1(), bar1()]}, W1, S0), - Expect = {stop, [bar1(), foo1()]}, - ?assertEqual(Expect, handle_message({ok, [foo1(), bar1()]}, W2, S1)) - end). - - -check_finish_quorum_newer() -> - % We count a descendant of a revision for quorum so - % foo1 should count for foo2 which means we're finished. - % We also validate that read_repair was triggered. - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - S0 = state0(all, false), - {ok, S1} = handle_message({ok, [foo1(), bar1()]}, W1, S0), - Expect = {stop, [bar1(), foo2()]}, - ok = meck:reset(fabric), - ?assertEqual(Expect, handle_message({ok, [foo2(), bar1()]}, W2, S1)), - ok = meck:wait(fabric, update_docs, '_', 5000), - ?assertMatch( - [{_, {fabric, update_docs, [_, _, _]}, _}], - meck:history(fabric) - ) - end). - - -check_no_quorum_on_second() -> - % Quorum not yet met for the foo revision so we - % would wait for w3 - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - W3 = #shard{node='node3'}, - S0 = state0(all, false), - {ok, S1} = handle_message({ok, [foo1(), bar1()]}, W1, S0), - ?assertMatch( - {ok, #state{workers = [W3]}}, - handle_message({ok, [bar1()]}, W2, S1) - ) - end). - - -check_done_on_third() -> - % The third message of three means we're done no matter - % what. Every revision seen in this pattern should be - % included. - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - W3 = #shard{node='node3'}, - S0 = state0(all, false), - {ok, S1} = handle_message({ok, [foo1(), bar1()]}, W1, S0), - {ok, S2} = handle_message({ok, [bar1()]}, W2, S1), - Expect = {stop, [bar1(), foo1()]}, - ?assertEqual(Expect, handle_message({ok, [bar1()]}, W3, S2)) - end). - - -% Tests for a specific list of revs - - -check_specific_revs_first_msg() -> - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - W3 = #shard{node='node3'}, - S0 = state0(revs(), false), - ?assertMatch( - {ok, #state{reply_count = 1, workers = [W2, W3]}}, - handle_message({ok, [foo1(), bar1(), bazNF()]}, W1, S0) - ) - end). - - -check_revs_done_on_agreement() -> - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - S0 = state0(revs(), false), - Msg = {ok, [foo1(), bar1(), bazNF()]}, - {ok, S1} = handle_message(Msg, W1, S0), - Expect = {stop, [bar1(), foo1(), bazNF()]}, - ?assertEqual(Expect, handle_message(Msg, W2, S1)) - end). - - -check_latest_true() -> - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - S0 = state0(revs(), true), - Msg1 = {ok, [foo2(), bar1(), bazNF()]}, - Msg2 = {ok, [foo2(), bar1(), bazNF()]}, - {ok, S1} = handle_message(Msg1, W1, S0), - Expect = {stop, [bar1(), foo2(), bazNF()]}, - ?assertEqual(Expect, handle_message(Msg2, W2, S1)) - end). - - -check_ancestor_counted_in_quorum() -> - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - S0 = state0(revs(), true), - Msg1 = {ok, [foo1(), bar1(), bazNF()]}, - Msg2 = {ok, [foo2(), bar1(), bazNF()]}, - Expect = {stop, [bar1(), foo2(), bazNF()]}, - - % Older first - {ok, S1} = handle_message(Msg1, W1, S0), - ?assertEqual(Expect, handle_message(Msg2, W2, S1)), - - % Newer first - {ok, S2} = handle_message(Msg2, W2, S0), - ?assertEqual(Expect, handle_message(Msg1, W1, S2)) - end). - - -check_not_found_counts_for_descendant() -> - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - S0 = state0(revs(), true), - Msg1 = {ok, [foo1(), bar1(), bazNF()]}, - Msg2 = {ok, [foo1(), bar1(), baz1()]}, - Expect = {stop, [bar1(), baz1(), foo1()]}, - - % not_found first - {ok, S1} = handle_message(Msg1, W1, S0), - ?assertEqual(Expect, handle_message(Msg2, W2, S1)), - - % not_found second - {ok, S2} = handle_message(Msg2, W2, S0), - ?assertEqual(Expect, handle_message(Msg1, W1, S2)) - end). - - -check_worker_error_skipped() -> - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - W3 = #shard{node='node3'}, - S0 = state0(revs(), true), - Msg1 = {ok, [foo1(), bar1(), baz1()]}, - Msg2 = {rexi_EXIT, reason}, - Msg3 = {ok, [foo1(), bar1(), baz1()]}, - Expect = {stop, [bar1(), baz1(), foo1()]}, - - {ok, S1} = handle_message(Msg1, W1, S0), - {ok, S2} = handle_message(Msg2, W2, S1), - ?assertEqual(Expect, handle_message(Msg3, W3, S2)) - end). - - -check_quorum_only_counts_valid_responses() -> - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - W3 = #shard{node='node3'}, - S0 = state0(revs(), true), - Msg1 = {rexi_EXIT, reason}, - Msg2 = {rexi_EXIT, reason}, - Msg3 = {ok, [foo1(), bar1(), baz1()]}, - Expect = {stop, [bar1(), baz1(), foo1()]}, - - {ok, S1} = handle_message(Msg1, W1, S0), - {ok, S2} = handle_message(Msg2, W2, S1), - ?assertEqual(Expect, handle_message(Msg3, W3, S2)) - end). - - -check_empty_list_when_no_workers_reply() -> - ?_test(begin - W1 = #shard{node='node1'}, - W2 = #shard{node='node2'}, - W3 = #shard{node='node3'}, - S0 = state0(revs(), true), - Msg1 = {rexi_EXIT, reason}, - Msg2 = {rexi_EXIT, reason}, - Msg3 = {rexi_DOWN, nodedown, {nil, node()}, nil}, - Expect = {stop, all_workers_died}, - - {ok, S1} = handle_message(Msg1, W1, S0), - {ok, S2} = handle_message(Msg2, W2, S1), - ?assertEqual(Expect, handle_message(Msg3, W3, S2)) - end). - - -check_node_rev_stored() -> - ?_test(begin - W1 = #shard{node = node1}, - S0 = state0([], true), - - {ok, S1} = handle_message({ok, [foo1()]}, W1, S0), - ?assertEqual([{node1, [{1, <<"foo">>}]}], S1#state.node_revs) - end). - - -check_node_rev_store_head_only() -> - ?_test(begin - W1 = #shard{node = node1}, - S0 = state0([], true), - - {ok, S1} = handle_message({ok, [foo2()]}, W1, S0), - ?assertEqual([{node1, [{2, <<"foo2">>}]}], S1#state.node_revs) - end). - - -check_node_rev_store_multiple() -> - ?_test(begin - W1 = #shard{node = node1}, - S0 = state0([], true), - - {ok, S1} = handle_message({ok, [foo1(), foo2()]}, W1, S0), - ?assertEqual( - [{node1, [{2, <<"foo2">>}, {1, <<"foo">>}]}], - S1#state.node_revs - ) - end). - - -check_node_rev_dont_store_errors() -> - ?_test(begin - W1 = #shard{node = node1}, - S0 = state0([], true), - - {ok, S1} = handle_message({ok, [barNF()]}, W1, S0), - ?assertEqual([], S1#state.node_revs) - end). - - -check_node_rev_store_non_errors() -> - ?_test(begin - W1 = #shard{node = node1}, - S0 = state0([], true), - - {ok, S1} = handle_message({ok, [foo1(), barNF()]}, W1, S0), - ?assertEqual([{node1, [{1, <<"foo">>}]}], S1#state.node_revs) - end). - - -check_node_rev_store_concatenate() -> - ?_test(begin - W2 = #shard{node = node2}, - S0 = state0([], true), - S1 = S0#state{node_revs = [{node1, [{1, <<"foo">>}]}]}, - - {ok, S2} = handle_message({ok, [foo2()]}, W2, S1), - ?assertEqual( - [{node2, [{2, <<"foo2">>}]}, {node1, [{1, <<"foo">>}]}], - S2#state.node_revs - ) - end). - - -check_node_rev_store_concantenate_multiple() -> - ?_test(begin - W2 = #shard{node = node2}, - S0 = state0([], true), - S1 = S0#state{node_revs = [{node1, [{1, <<"foo">>}]}]}, - - {ok, S2} = handle_message({ok, [foo2(), bar1()]}, W2, S1), - ?assertEqual( - [ - {node2, [{1, <<"bar">>}, {2, <<"foo2">>}]}, - {node1, [{1, <<"foo">>}]} - ], - S2#state.node_revs - ) - end). - - -check_node_rev_unmodified_on_down_or_exit() -> - ?_test(begin - W2 = #shard{node = node2}, - S0 = state0([], true), - S1 = S0#state{node_revs = [{node1, [{1, <<"foo">>}]}]}, - - Down = {rexi_DOWN, nodedown, {nil, node()}, nil}, - {ok, S2} = handle_message(Down, W2, S1), - ?assertEqual( - [{node1, [{1, <<"foo">>}]}], - S2#state.node_revs - ), - - Exit = {rexi_EXIT, reason}, - {ok, S3} = handle_message(Exit, W2, S1), - ?assertEqual( - [{node1, [{1, <<"foo">>}]}], - S3#state.node_revs - ) - end). - - -check_not_found_replies_are_removed_when_doc_found() -> - ?_test(begin - Replies = replies_to_dict([foo1(), bar1(), fooNF()]), - Expect = [bar1(), foo1()], - ?assertEqual(Expect, dict_format_replies(Replies)) - end). - -check_not_found_returned_when_one_of_docs_not_found() -> - ?_test(begin - Replies = replies_to_dict([foo1(), foo2(), barNF()]), - Expect = [foo1(), foo2(), barNF()], - ?assertEqual(Expect, dict_format_replies(Replies)) - end). - -check_not_found_returned_when_doc_not_found() -> - ?_test(begin - Replies = replies_to_dict([fooNF(), barNF(), bazNF()]), - Expect = [barNF(), bazNF(), fooNF()], - ?assertEqual(Expect, dict_format_replies(Replies)) - end). - -check_longer_rev_list_returned() -> - ?_test(begin - Replies = replies_to_dict([foo2(), foo2stemmed()]), - Expect = [foo2()], - ?assertEqual(2, length(Replies)), - ?assertEqual(Expect, dict_format_replies(Replies)) - end). - -check_longer_rev_list_not_combined() -> - ?_test(begin - Replies = replies_to_dict([foo2(), foo2stemmed(), bar1()]), - Expect = [bar1(), foo2()], - ?assertEqual(3, length(Replies)), - ?assertEqual(Expect, dict_format_replies(Replies)) - end). - -check_not_found_removed_and_longer_rev_list() -> - ?_test(begin - Replies = replies_to_dict([foo2(), foo2stemmed(), foo2NF()]), - Expect = [foo2()], - ?assertEqual(3, length(Replies)), - ?assertEqual(Expect, dict_format_replies(Replies)) - end). - - -replies_to_dict(Replies) -> - [reply_to_element(R) || R <- Replies]. - -reply_to_element({ok, #doc{revs = Revs}} = Reply) -> - {_, [Rev | _]} = Revs, - {{Rev, Revs}, {Reply, 1}}; -reply_to_element(Reply) -> - {Reply, {Reply, 1}}. - --endif. +%% -ifdef(TEST). +%% -include_lib("eunit/include/eunit.hrl"). +%% +%% +%% setup() -> +%% config:start_link([]), +%% meck:new([fabric, couch_stats, couch_log]), +%% meck:new(fabric_util, [passthrough]), +%% meck:expect(fabric, update_docs, fun(_, _, _) -> {ok, nil} end), +%% meck:expect(couch_stats, increment_counter, fun(_) -> ok end), +%% meck:expect(couch_log, notice, fun(_, _) -> ok end), +%% meck:expect(fabric_util, cleanup, fun(_) -> ok end). +%% +%% +%% +%% teardown(_) -> +%% (catch meck:unload([fabric, couch_stats, couch_log, fabric_util])), +%% config:stop(). +%% +%% +%% state0(Revs, Latest) -> +%% #state{ +%% worker_count = 3, +%% workers = +%% [#shard{node='node1'}, #shard{node='node2'}, #shard{node='node3'}], +%% r = 2, +%% revs = Revs, +%% latest = Latest +%% }. +%% +%% +%% revs() -> [{1,<<"foo">>}, {1,<<"bar">>}, {1,<<"baz">>}]. +%% +%% +%% foo1() -> {ok, #doc{revs = {1, [<<"foo">>]}}}. +%% foo2() -> {ok, #doc{revs = {2, [<<"foo2">>, <<"foo">>]}}}. +%% foo2stemmed() -> {ok, #doc{revs = {2, [<<"foo2">>]}}}. +%% fooNF() -> {{not_found, missing}, {1,<<"foo">>}}. +%% foo2NF() -> {{not_found, missing}, {2, <<"foo2">>}}. +%% bar1() -> {ok, #doc{revs = {1, [<<"bar">>]}}}. +%% barNF() -> {{not_found, missing}, {1,<<"bar">>}}. +%% bazNF() -> {{not_found, missing}, {1,<<"baz">>}}. +%% baz1() -> {ok, #doc{revs = {1, [<<"baz">>]}}}. +%% +%% +%% +%% open_doc_revs_test_() -> +%% { +%% foreach, +%% fun setup/0, +%% fun teardown/1, +%% [ +%% check_empty_response_not_quorum(), +%% check_basic_response(), +%% check_finish_quorum(), +%% check_finish_quorum_newer(), +%% check_no_quorum_on_second(), +%% check_done_on_third(), +%% check_specific_revs_first_msg(), +%% check_revs_done_on_agreement(), +%% check_latest_true(), +%% check_ancestor_counted_in_quorum(), +%% check_not_found_counts_for_descendant(), +%% check_worker_error_skipped(), +%% check_quorum_only_counts_valid_responses(), +%% check_empty_list_when_no_workers_reply(), +%% check_node_rev_stored(), +%% check_node_rev_store_head_only(), +%% check_node_rev_store_multiple(), +%% check_node_rev_dont_store_errors(), +%% check_node_rev_store_non_errors(), +%% check_node_rev_store_concatenate(), +%% check_node_rev_store_concantenate_multiple(), +%% check_node_rev_unmodified_on_down_or_exit(), +%% check_not_found_replies_are_removed_when_doc_found(), +%% check_not_found_returned_when_one_of_docs_not_found(), +%% check_not_found_returned_when_doc_not_found(), +%% check_longer_rev_list_returned(), +%% check_longer_rev_list_not_combined(), +%% check_not_found_removed_and_longer_rev_list() +%% ] +%% }. +%% +%% +%% % Tests for revs=all +%% +%% +%% check_empty_response_not_quorum() -> +%% % Simple smoke test that we don't think we're +%% % done with a first empty response +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% W3 = #shard{node='node3'}, +%% ?_assertMatch( +%% {ok, #state{workers = [W2, W3]}}, +%% handle_message({ok, []}, W1, state0(all, false)) +%% ). +%% +%% +%% check_basic_response() -> +%% % Check that we've handle a response +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% W3 = #shard{node='node3'}, +%% ?_assertMatch( +%% {ok, #state{reply_count = 1, workers = [W2, W3]}}, +%% handle_message({ok, [foo1(), bar1()]}, W1, state0(all, false)) +%% ). +%% +%% +%% check_finish_quorum() -> +%% % Two messages with the same revisions means we're done +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% S0 = state0(all, false), +%% {ok, S1} = handle_message({ok, [foo1(), bar1()]}, W1, S0), +%% Expect = {stop, [bar1(), foo1()]}, +%% ?assertEqual(Expect, handle_message({ok, [foo1(), bar1()]}, W2, S1)) +%% end). +%% +%% +%% check_finish_quorum_newer() -> +%% % We count a descendant of a revision for quorum so +%% % foo1 should count for foo2 which means we're finished. +%% % We also validate that read_repair was triggered. +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% S0 = state0(all, false), +%% {ok, S1} = handle_message({ok, [foo1(), bar1()]}, W1, S0), +%% Expect = {stop, [bar1(), foo2()]}, +%% ok = meck:reset(fabric), +%% ?assertEqual(Expect, handle_message({ok, [foo2(), bar1()]}, W2, S1)), +%% ok = meck:wait(fabric, update_docs, '_', 5000), +%% ?assertMatch( +%% [{_, {fabric, update_docs, [_, _, _]}, _}], +%% meck:history(fabric) +%% ) +%% end). +%% +%% +%% check_no_quorum_on_second() -> +%% % Quorum not yet met for the foo revision so we +%% % would wait for w3 +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% W3 = #shard{node='node3'}, +%% S0 = state0(all, false), +%% {ok, S1} = handle_message({ok, [foo1(), bar1()]}, W1, S0), +%% ?assertMatch( +%% {ok, #state{workers = [W3]}}, +%% handle_message({ok, [bar1()]}, W2, S1) +%% ) +%% end). +%% +%% +%% check_done_on_third() -> +%% % The third message of three means we're done no matter +%% % what. Every revision seen in this pattern should be +%% % included. +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% W3 = #shard{node='node3'}, +%% S0 = state0(all, false), +%% {ok, S1} = handle_message({ok, [foo1(), bar1()]}, W1, S0), +%% {ok, S2} = handle_message({ok, [bar1()]}, W2, S1), +%% Expect = {stop, [bar1(), foo1()]}, +%% ?assertEqual(Expect, handle_message({ok, [bar1()]}, W3, S2)) +%% end). +%% +%% +%% % Tests for a specific list of revs +%% +%% +%% check_specific_revs_first_msg() -> +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% W3 = #shard{node='node3'}, +%% S0 = state0(revs(), false), +%% ?assertMatch( +%% {ok, #state{reply_count = 1, workers = [W2, W3]}}, +%% handle_message({ok, [foo1(), bar1(), bazNF()]}, W1, S0) +%% ) +%% end). +%% +%% +%% check_revs_done_on_agreement() -> +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% S0 = state0(revs(), false), +%% Msg = {ok, [foo1(), bar1(), bazNF()]}, +%% {ok, S1} = handle_message(Msg, W1, S0), +%% Expect = {stop, [bar1(), foo1(), bazNF()]}, +%% ?assertEqual(Expect, handle_message(Msg, W2, S1)) +%% end). +%% +%% +%% check_latest_true() -> +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% S0 = state0(revs(), true), +%% Msg1 = {ok, [foo2(), bar1(), bazNF()]}, +%% Msg2 = {ok, [foo2(), bar1(), bazNF()]}, +%% {ok, S1} = handle_message(Msg1, W1, S0), +%% Expect = {stop, [bar1(), foo2(), bazNF()]}, +%% ?assertEqual(Expect, handle_message(Msg2, W2, S1)) +%% end). +%% +%% +%% check_ancestor_counted_in_quorum() -> +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% S0 = state0(revs(), true), +%% Msg1 = {ok, [foo1(), bar1(), bazNF()]}, +%% Msg2 = {ok, [foo2(), bar1(), bazNF()]}, +%% Expect = {stop, [bar1(), foo2(), bazNF()]}, +%% +%% % Older first +%% {ok, S1} = handle_message(Msg1, W1, S0), +%% ?assertEqual(Expect, handle_message(Msg2, W2, S1)), +%% +%% % Newer first +%% {ok, S2} = handle_message(Msg2, W2, S0), +%% ?assertEqual(Expect, handle_message(Msg1, W1, S2)) +%% end). +%% +%% +%% check_not_found_counts_for_descendant() -> +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% S0 = state0(revs(), true), +%% Msg1 = {ok, [foo1(), bar1(), bazNF()]}, +%% Msg2 = {ok, [foo1(), bar1(), baz1()]}, +%% Expect = {stop, [bar1(), baz1(), foo1()]}, +%% +%% % not_found first +%% {ok, S1} = handle_message(Msg1, W1, S0), +%% ?assertEqual(Expect, handle_message(Msg2, W2, S1)), +%% +%% % not_found second +%% {ok, S2} = handle_message(Msg2, W2, S0), +%% ?assertEqual(Expect, handle_message(Msg1, W1, S2)) +%% end). +%% +%% +%% check_worker_error_skipped() -> +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% W3 = #shard{node='node3'}, +%% S0 = state0(revs(), true), +%% Msg1 = {ok, [foo1(), bar1(), baz1()]}, +%% Msg2 = {rexi_EXIT, reason}, +%% Msg3 = {ok, [foo1(), bar1(), baz1()]}, +%% Expect = {stop, [bar1(), baz1(), foo1()]}, +%% +%% {ok, S1} = handle_message(Msg1, W1, S0), +%% {ok, S2} = handle_message(Msg2, W2, S1), +%% ?assertEqual(Expect, handle_message(Msg3, W3, S2)) +%% end). +%% +%% +%% check_quorum_only_counts_valid_responses() -> +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% W3 = #shard{node='node3'}, +%% S0 = state0(revs(), true), +%% Msg1 = {rexi_EXIT, reason}, +%% Msg2 = {rexi_EXIT, reason}, +%% Msg3 = {ok, [foo1(), bar1(), baz1()]}, +%% Expect = {stop, [bar1(), baz1(), foo1()]}, +%% +%% {ok, S1} = handle_message(Msg1, W1, S0), +%% {ok, S2} = handle_message(Msg2, W2, S1), +%% ?assertEqual(Expect, handle_message(Msg3, W3, S2)) +%% end). +%% +%% +%% check_empty_list_when_no_workers_reply() -> +%% ?_test(begin +%% W1 = #shard{node='node1'}, +%% W2 = #shard{node='node2'}, +%% W3 = #shard{node='node3'}, +%% S0 = state0(revs(), true), +%% Msg1 = {rexi_EXIT, reason}, +%% Msg2 = {rexi_EXIT, reason}, +%% Msg3 = {rexi_DOWN, nodedown, {nil, node()}, nil}, +%% Expect = {stop, all_workers_died}, +%% +%% {ok, S1} = handle_message(Msg1, W1, S0), +%% {ok, S2} = handle_message(Msg2, W2, S1), +%% ?assertEqual(Expect, handle_message(Msg3, W3, S2)) +%% end). +%% +%% +%% check_node_rev_stored() -> +%% ?_test(begin +%% W1 = #shard{node = node1}, +%% S0 = state0([], true), +%% +%% {ok, S1} = handle_message({ok, [foo1()]}, W1, S0), +%% ?assertEqual([{node1, [{1, <<"foo">>}]}], S1#state.node_revs) +%% end). +%% +%% +%% check_node_rev_store_head_only() -> +%% ?_test(begin +%% W1 = #shard{node = node1}, +%% S0 = state0([], true), +%% +%% {ok, S1} = handle_message({ok, [foo2()]}, W1, S0), +%% ?assertEqual([{node1, [{2, <<"foo2">>}]}], S1#state.node_revs) +%% end). +%% +%% +%% check_node_rev_store_multiple() -> +%% ?_test(begin +%% W1 = #shard{node = node1}, +%% S0 = state0([], true), +%% +%% {ok, S1} = handle_message({ok, [foo1(), foo2()]}, W1, S0), +%% ?assertEqual( +%% [{node1, [{2, <<"foo2">>}, {1, <<"foo">>}]}], +%% S1#state.node_revs +%% ) +%% end). +%% +%% +%% check_node_rev_dont_store_errors() -> +%% ?_test(begin +%% W1 = #shard{node = node1}, +%% S0 = state0([], true), +%% +%% {ok, S1} = handle_message({ok, [barNF()]}, W1, S0), +%% ?assertEqual([], S1#state.node_revs) +%% end). +%% +%% +%% check_node_rev_store_non_errors() -> +%% ?_test(begin +%% W1 = #shard{node = node1}, +%% S0 = state0([], true), +%% +%% {ok, S1} = handle_message({ok, [foo1(), barNF()]}, W1, S0), +%% ?assertEqual([{node1, [{1, <<"foo">>}]}], S1#state.node_revs) +%% end). +%% +%% +%% check_node_rev_store_concatenate() -> +%% ?_test(begin +%% W2 = #shard{node = node2}, +%% S0 = state0([], true), +%% S1 = S0#state{node_revs = [{node1, [{1, <<"foo">>}]}]}, +%% +%% {ok, S2} = handle_message({ok, [foo2()]}, W2, S1), +%% ?assertEqual( +%% [{node2, [{2, <<"foo2">>}]}, {node1, [{1, <<"foo">>}]}], +%% S2#state.node_revs +%% ) +%% end). +%% +%% +%% check_node_rev_store_concantenate_multiple() -> +%% ?_test(begin +%% W2 = #shard{node = node2}, +%% S0 = state0([], true), +%% S1 = S0#state{node_revs = [{node1, [{1, <<"foo">>}]}]}, +%% +%% {ok, S2} = handle_message({ok, [foo2(), bar1()]}, W2, S1), +%% ?assertEqual( +%% [ +%% {node2, [{1, <<"bar">>}, {2, <<"foo2">>}]}, +%% {node1, [{1, <<"foo">>}]} +%% ], +%% S2#state.node_revs +%% ) +%% end). +%% +%% +%% check_node_rev_unmodified_on_down_or_exit() -> +%% ?_test(begin +%% W2 = #shard{node = node2}, +%% S0 = state0([], true), +%% S1 = S0#state{node_revs = [{node1, [{1, <<"foo">>}]}]}, +%% +%% Down = {rexi_DOWN, nodedown, {nil, node()}, nil}, +%% {ok, S2} = handle_message(Down, W2, S1), +%% ?assertEqual( +%% [{node1, [{1, <<"foo">>}]}], +%% S2#state.node_revs +%% ), +%% +%% Exit = {rexi_EXIT, reason}, +%% {ok, S3} = handle_message(Exit, W2, S1), +%% ?assertEqual( +%% [{node1, [{1, <<"foo">>}]}], +%% S3#state.node_revs +%% ) +%% end). +%% +%% +%% check_not_found_replies_are_removed_when_doc_found() -> +%% ?_test(begin +%% Replies = replies_to_dict([foo1(), bar1(), fooNF()]), +%% Expect = [bar1(), foo1()], +%% ?assertEqual(Expect, dict_format_replies(Replies)) +%% end). +%% +%% check_not_found_returned_when_one_of_docs_not_found() -> +%% ?_test(begin +%% Replies = replies_to_dict([foo1(), foo2(), barNF()]), +%% Expect = [foo1(), foo2(), barNF()], +%% ?assertEqual(Expect, dict_format_replies(Replies)) +%% end). +%% +%% check_not_found_returned_when_doc_not_found() -> +%% ?_test(begin +%% Replies = replies_to_dict([fooNF(), barNF(), bazNF()]), +%% Expect = [barNF(), bazNF(), fooNF()], +%% ?assertEqual(Expect, dict_format_replies(Replies)) +%% end). +%% +%% check_longer_rev_list_returned() -> +%% ?_test(begin +%% Replies = replies_to_dict([foo2(), foo2stemmed()]), +%% Expect = [foo2()], +%% ?assertEqual(2, length(Replies)), +%% ?assertEqual(Expect, dict_format_replies(Replies)) +%% end). +%% +%% check_longer_rev_list_not_combined() -> +%% ?_test(begin +%% Replies = replies_to_dict([foo2(), foo2stemmed(), bar1()]), +%% Expect = [bar1(), foo2()], +%% ?assertEqual(3, length(Replies)), +%% ?assertEqual(Expect, dict_format_replies(Replies)) +%% end). +%% +%% check_not_found_removed_and_longer_rev_list() -> +%% ?_test(begin +%% Replies = replies_to_dict([foo2(), foo2stemmed(), foo2NF()]), +%% Expect = [foo2()], +%% ?assertEqual(3, length(Replies)), +%% ?assertEqual(Expect, dict_format_replies(Replies)) +%% end). +%% +%% +%% replies_to_dict(Replies) -> +%% [reply_to_element(R) || R <- Replies]. +%% +%% reply_to_element({ok, #doc{revs = Revs}} = Reply) -> +%% {_, [Rev | _]} = Revs, +%% {{Rev, Revs}, {Reply, 1}}; +%% reply_to_element(Reply) -> +%% {Reply, {Reply, 1}}. +%% +%% -endif. diff --git a/src/fabric/src/fabric_doc_purge.erl b/src/fabric/src/fabric_doc_purge.erl index 7e447ff1b..6d77fc2f7 100644 --- a/src/fabric/src/fabric_doc_purge.erl +++ b/src/fabric/src/fabric_doc_purge.erl @@ -224,349 +224,349 @@ has_quorum(Resps, Count, W) -> end. --ifdef(TEST). - --include_lib("eunit/include/eunit.hrl"). - -purge_test_() -> - { - foreach, - fun setup/0, - fun teardown/1, - [ - t_w2_ok(), - t_w3_ok(), - - t_w2_mixed_accepted(), - t_w3_mixed_accepted(), - - t_w2_exit1_ok(), - t_w2_exit2_accepted(), - t_w2_exit3_error(), - - t_w4_accepted(), - - t_mixed_ok_accepted(), - t_mixed_errors() - ] - }. - - -setup() -> - meck:new(couch_log), - meck:expect(couch_log, warning, fun(_, _) -> ok end), - meck:expect(couch_log, notice, fun(_, _) -> ok end). - - -teardown(_) -> - meck:unload(). - - -t_w2_ok() -> - ?_test(begin - Acc0 = create_init_acc(2), - Msg = {ok, [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}]}, - - {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), - ?assertEqual(2, length(Acc1#acc.worker_uuids)), - check_quorum(Acc1, false), - - {stop, Acc2} = handle_message(Msg, worker(2, Acc0), Acc1), - ?assertEqual(1, length(Acc2#acc.worker_uuids)), - check_quorum(Acc2, true), - - Expect = [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}], - Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc2), - ?assertEqual(Expect, Resps), - ?assertEqual(ok, resp_health(Resps)) - end). - - -t_w3_ok() -> - ?_test(begin - Acc0 = create_init_acc(3), - Msg = {ok, [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}]}, - - {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), - check_quorum(Acc1, false), - - {ok, Acc2} = handle_message(Msg, worker(2, Acc0), Acc1), - ?assertEqual(1, length(Acc2#acc.worker_uuids)), - check_quorum(Acc2, false), - - {stop, Acc3} = handle_message(Msg, worker(3, Acc0), Acc2), - ?assertEqual(0, length(Acc3#acc.worker_uuids)), - check_quorum(Acc3, true), - - Expect = [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}], - Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc3), - ?assertEqual(Expect, Resps), - ?assertEqual(ok, resp_health(Resps)) - end). - - -t_w2_mixed_accepted() -> - ?_test(begin - Acc0 = create_init_acc(2), - Msg1 = {ok, [{ok, [{1, <<"foo1">>}]}, {ok, [{2, <<"bar1">>}]}]}, - Msg2 = {ok, [{ok, [{1, <<"foo2">>}]}, {ok, [{2, <<"bar2">>}]}]}, - - {ok, Acc1} = handle_message(Msg1, worker(1, Acc0), Acc0), - ?assertEqual(2, length(Acc1#acc.worker_uuids)), - check_quorum(Acc1, false), - - {ok, Acc2} = handle_message(Msg2, worker(2, Acc0), Acc1), - ?assertEqual(1, length(Acc2#acc.worker_uuids)), - check_quorum(Acc2, false), - - {stop, Acc3} = handle_message(Msg1, worker(3, Acc0), Acc2), - ?assertEqual(0, length(Acc3#acc.worker_uuids)), - check_quorum(Acc3, true), - - Expect = [ - {accepted, [{1, <<"foo1">>}, {1, <<"foo2">>}]}, - {accepted, [{2, <<"bar1">>}, {2, <<"bar2">>}]} - ], - Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc2), - ?assertEqual(Expect, Resps), - ?assertEqual(accepted, resp_health(Resps)) - end). - - -t_w3_mixed_accepted() -> - ?_test(begin - Acc0 = create_init_acc(3), - Msg1 = {ok, [{ok, [{1, <<"foo1">>}]}, {ok, [{2, <<"bar1">>}]}]}, - Msg2 = {ok, [{ok, [{1, <<"foo2">>}]}, {ok, [{2, <<"bar2">>}]}]}, - - {ok, Acc1} = handle_message(Msg1, worker(1, Acc0), Acc0), - ?assertEqual(2, length(Acc1#acc.worker_uuids)), - check_quorum(Acc1, false), - - {ok, Acc2} = handle_message(Msg2, worker(2, Acc0), Acc1), - ?assertEqual(1, length(Acc2#acc.worker_uuids)), - check_quorum(Acc2, false), - - {stop, Acc3} = handle_message(Msg2, worker(3, Acc0), Acc2), - ?assertEqual(0, length(Acc3#acc.worker_uuids)), - check_quorum(Acc3, true), - - Expect = [ - {accepted, [{1, <<"foo1">>}, {1, <<"foo2">>}]}, - {accepted, [{2, <<"bar1">>}, {2, <<"bar2">>}]} - ], - Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc2), - ?assertEqual(Expect, Resps), - ?assertEqual(accepted, resp_health(Resps)) - end). - - -t_w2_exit1_ok() -> - ?_test(begin - Acc0 = create_init_acc(2), - Msg = {ok, [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}]}, - ExitMsg = {rexi_EXIT, blargh}, - - {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), - ?assertEqual(2, length(Acc1#acc.worker_uuids)), - check_quorum(Acc1, false), - - {ok, Acc2} = handle_message(ExitMsg, worker(2, Acc0), Acc1), - ?assertEqual(1, length(Acc2#acc.worker_uuids)), - check_quorum(Acc2, false), - - {stop, Acc3} = handle_message(Msg, worker(3, Acc0), Acc2), - ?assertEqual(0, length(Acc3#acc.worker_uuids)), - check_quorum(Acc3, true), - - Expect = [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}], - Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc3), - ?assertEqual(Expect, Resps), - ?assertEqual(ok, resp_health(Resps)) - end). - - -t_w2_exit2_accepted() -> - ?_test(begin - Acc0 = create_init_acc(2), - Msg = {ok, [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}]}, - ExitMsg = {rexi_EXIT, blargh}, - - {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), - ?assertEqual(2, length(Acc1#acc.worker_uuids)), - check_quorum(Acc1, false), - - {ok, Acc2} = handle_message(ExitMsg, worker(2, Acc0), Acc1), - ?assertEqual(1, length(Acc2#acc.worker_uuids)), - check_quorum(Acc2, false), - - {stop, Acc3} = handle_message(ExitMsg, worker(3, Acc0), Acc2), - ?assertEqual(0, length(Acc3#acc.worker_uuids)), - check_quorum(Acc3, true), - - Expect = [{accepted, [{1, <<"foo">>}]}, {accepted, [{2, <<"bar">>}]}], - Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc3), - ?assertEqual(Expect, Resps), - ?assertEqual(accepted, resp_health(Resps)) - end). - - -t_w2_exit3_error() -> - ?_test(begin - Acc0 = create_init_acc(2), - ExitMsg = {rexi_EXIT, blargh}, - - {ok, Acc1} = handle_message(ExitMsg, worker(1, Acc0), Acc0), - ?assertEqual(2, length(Acc1#acc.worker_uuids)), - check_quorum(Acc1, false), - - {ok, Acc2} = handle_message(ExitMsg, worker(2, Acc0), Acc1), - ?assertEqual(1, length(Acc2#acc.worker_uuids)), - check_quorum(Acc2, false), - - {stop, Acc3} = handle_message(ExitMsg, worker(3, Acc0), Acc2), - ?assertEqual(0, length(Acc3#acc.worker_uuids)), - check_quorum(Acc3, true), - - Expect = [ - {error, internal_server_error}, - {error, internal_server_error} - ], - Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc3), - ?assertEqual(Expect, Resps), - ?assertEqual(error, resp_health(Resps)) - end). - - -t_w4_accepted() -> - % Make sure we return when all workers have responded - % rather than wait around for a timeout if a user asks - % for a qourum with more than the available number of - % shards. - ?_test(begin - Acc0 = create_init_acc(4), - Msg = {ok, [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}]}, - - {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), - ?assertEqual(2, length(Acc1#acc.worker_uuids)), - check_quorum(Acc1, false), - - {ok, Acc2} = handle_message(Msg, worker(2, Acc0), Acc1), - ?assertEqual(1, length(Acc2#acc.worker_uuids)), - check_quorum(Acc2, false), - - {stop, Acc3} = handle_message(Msg, worker(3, Acc0), Acc2), - ?assertEqual(0, length(Acc3#acc.worker_uuids)), - check_quorum(Acc3, true), - - Expect = [{accepted, [{1, <<"foo">>}]}, {accepted, [{2, <<"bar">>}]}], - Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc3), - ?assertEqual(Expect, Resps), - ?assertEqual(accepted, resp_health(Resps)) - end). - - -t_mixed_ok_accepted() -> - ?_test(begin - WorkerUUIDs = [ - {#shard{node = a, range = [1, 2]}, [<<"uuid1">>]}, - {#shard{node = b, range = [1, 2]}, [<<"uuid1">>]}, - {#shard{node = c, range = [1, 2]}, [<<"uuid1">>]}, - - {#shard{node = a, range = [3, 4]}, [<<"uuid2">>]}, - {#shard{node = b, range = [3, 4]}, [<<"uuid2">>]}, - {#shard{node = c, range = [3, 4]}, [<<"uuid2">>]} - ], - - Acc0 = #acc{ - worker_uuids = WorkerUUIDs, - resps = dict:from_list([{<<"uuid1">>, []}, {<<"uuid2">>, []}]), - uuid_counts = dict:from_list([{<<"uuid1">>, 3}, {<<"uuid2">>, 3}]), - w = 2 - }, - - Msg1 = {ok, [{ok, [{1, <<"foo">>}]}]}, - Msg2 = {ok, [{ok, [{2, <<"bar">>}]}]}, - ExitMsg = {rexi_EXIT, blargh}, - - {ok, Acc1} = handle_message(Msg1, worker(1, Acc0), Acc0), - {ok, Acc2} = handle_message(Msg1, worker(2, Acc0), Acc1), - {ok, Acc3} = handle_message(ExitMsg, worker(4, Acc0), Acc2), - {ok, Acc4} = handle_message(ExitMsg, worker(5, Acc0), Acc3), - {stop, Acc5} = handle_message(Msg2, worker(6, Acc0), Acc4), - - Expect = [{ok, [{1, <<"foo">>}]}, {accepted, [{2, <<"bar">>}]}], - Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc5), - ?assertEqual(Expect, Resps), - ?assertEqual(accepted, resp_health(Resps)) - end). - - -t_mixed_errors() -> - ?_test(begin - WorkerUUIDs = [ - {#shard{node = a, range = [1, 2]}, [<<"uuid1">>]}, - {#shard{node = b, range = [1, 2]}, [<<"uuid1">>]}, - {#shard{node = c, range = [1, 2]}, [<<"uuid1">>]}, - - {#shard{node = a, range = [3, 4]}, [<<"uuid2">>]}, - {#shard{node = b, range = [3, 4]}, [<<"uuid2">>]}, - {#shard{node = c, range = [3, 4]}, [<<"uuid2">>]} - ], - - Acc0 = #acc{ - worker_uuids = WorkerUUIDs, - resps = dict:from_list([{<<"uuid1">>, []}, {<<"uuid2">>, []}]), - uuid_counts = dict:from_list([{<<"uuid1">>, 3}, {<<"uuid2">>, 3}]), - w = 2 - }, - - Msg = {ok, [{ok, [{1, <<"foo">>}]}]}, - ExitMsg = {rexi_EXIT, blargh}, - - {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), - {ok, Acc2} = handle_message(Msg, worker(2, Acc0), Acc1), - {ok, Acc3} = handle_message(ExitMsg, worker(4, Acc0), Acc2), - {ok, Acc4} = handle_message(ExitMsg, worker(5, Acc0), Acc3), - {stop, Acc5} = handle_message(ExitMsg, worker(6, Acc0), Acc4), - - Expect = [{ok, [{1, <<"foo">>}]}, {error, internal_server_error}], - Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc5), - ?assertEqual(Expect, Resps), - ?assertEqual(error, resp_health(Resps)) - end). - - -create_init_acc(W) -> - UUID1 = <<"uuid1">>, - UUID2 = <<"uuid2">>, - - Nodes = [node1, node2, node3], - Shards = mem3_util:create_partition_map(<<"foo">>, 3, 1, Nodes), - - % Create our worker_uuids. We're relying on the fact that - % we're using a fake Q=1 db so we don't have to worry - % about any hashing here. - WorkerUUIDs = lists:map(fun(Shard) -> - {Shard#shard{ref = erlang:make_ref()}, [UUID1, UUID2]} - end, Shards), - - #acc{ - worker_uuids = WorkerUUIDs, - resps = dict:from_list([{UUID1, []}, {UUID2, []}]), - uuid_counts = dict:from_list([{UUID1, 3}, {UUID2, 3}]), - w = W - }. - - -worker(N, #acc{worker_uuids = WorkerUUIDs}) -> - {Worker, _} = lists:nth(N, WorkerUUIDs), - Worker. - - -check_quorum(Acc, Expect) -> - dict:fold(fun(_Shard, Resps, _) -> - ?assertEqual(Expect, has_quorum(Resps, 3, Acc#acc.w)) - end, nil, Acc#acc.resps). - --endif. +%% -ifdef(TEST). +%% +%% -include_lib("eunit/include/eunit.hrl"). +%% +%% purge_test_() -> +%% { +%% foreach, +%% fun setup/0, +%% fun teardown/1, +%% [ +%% t_w2_ok(), +%% t_w3_ok(), +%% +%% t_w2_mixed_accepted(), +%% t_w3_mixed_accepted(), +%% +%% t_w2_exit1_ok(), +%% t_w2_exit2_accepted(), +%% t_w2_exit3_error(), +%% +%% t_w4_accepted(), +%% +%% t_mixed_ok_accepted(), +%% t_mixed_errors() +%% ] +%% }. +%% +%% +%% setup() -> +%% meck:new(couch_log), +%% meck:expect(couch_log, warning, fun(_, _) -> ok end), +%% meck:expect(couch_log, notice, fun(_, _) -> ok end). +%% +%% +%% teardown(_) -> +%% meck:unload(). +%% +%% +%% t_w2_ok() -> +%% ?_test(begin +%% Acc0 = create_init_acc(2), +%% Msg = {ok, [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}]}, +%% +%% {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), +%% ?assertEqual(2, length(Acc1#acc.worker_uuids)), +%% check_quorum(Acc1, false), +%% +%% {stop, Acc2} = handle_message(Msg, worker(2, Acc0), Acc1), +%% ?assertEqual(1, length(Acc2#acc.worker_uuids)), +%% check_quorum(Acc2, true), +%% +%% Expect = [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}], +%% Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc2), +%% ?assertEqual(Expect, Resps), +%% ?assertEqual(ok, resp_health(Resps)) +%% end). +%% +%% +%% t_w3_ok() -> +%% ?_test(begin +%% Acc0 = create_init_acc(3), +%% Msg = {ok, [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}]}, +%% +%% {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), +%% check_quorum(Acc1, false), +%% +%% {ok, Acc2} = handle_message(Msg, worker(2, Acc0), Acc1), +%% ?assertEqual(1, length(Acc2#acc.worker_uuids)), +%% check_quorum(Acc2, false), +%% +%% {stop, Acc3} = handle_message(Msg, worker(3, Acc0), Acc2), +%% ?assertEqual(0, length(Acc3#acc.worker_uuids)), +%% check_quorum(Acc3, true), +%% +%% Expect = [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}], +%% Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc3), +%% ?assertEqual(Expect, Resps), +%% ?assertEqual(ok, resp_health(Resps)) +%% end). +%% +%% +%% t_w2_mixed_accepted() -> +%% ?_test(begin +%% Acc0 = create_init_acc(2), +%% Msg1 = {ok, [{ok, [{1, <<"foo1">>}]}, {ok, [{2, <<"bar1">>}]}]}, +%% Msg2 = {ok, [{ok, [{1, <<"foo2">>}]}, {ok, [{2, <<"bar2">>}]}]}, +%% +%% {ok, Acc1} = handle_message(Msg1, worker(1, Acc0), Acc0), +%% ?assertEqual(2, length(Acc1#acc.worker_uuids)), +%% check_quorum(Acc1, false), +%% +%% {ok, Acc2} = handle_message(Msg2, worker(2, Acc0), Acc1), +%% ?assertEqual(1, length(Acc2#acc.worker_uuids)), +%% check_quorum(Acc2, false), +%% +%% {stop, Acc3} = handle_message(Msg1, worker(3, Acc0), Acc2), +%% ?assertEqual(0, length(Acc3#acc.worker_uuids)), +%% check_quorum(Acc3, true), +%% +%% Expect = [ +%% {accepted, [{1, <<"foo1">>}, {1, <<"foo2">>}]}, +%% {accepted, [{2, <<"bar1">>}, {2, <<"bar2">>}]} +%% ], +%% Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc2), +%% ?assertEqual(Expect, Resps), +%% ?assertEqual(accepted, resp_health(Resps)) +%% end). +%% +%% +%% t_w3_mixed_accepted() -> +%% ?_test(begin +%% Acc0 = create_init_acc(3), +%% Msg1 = {ok, [{ok, [{1, <<"foo1">>}]}, {ok, [{2, <<"bar1">>}]}]}, +%% Msg2 = {ok, [{ok, [{1, <<"foo2">>}]}, {ok, [{2, <<"bar2">>}]}]}, +%% +%% {ok, Acc1} = handle_message(Msg1, worker(1, Acc0), Acc0), +%% ?assertEqual(2, length(Acc1#acc.worker_uuids)), +%% check_quorum(Acc1, false), +%% +%% {ok, Acc2} = handle_message(Msg2, worker(2, Acc0), Acc1), +%% ?assertEqual(1, length(Acc2#acc.worker_uuids)), +%% check_quorum(Acc2, false), +%% +%% {stop, Acc3} = handle_message(Msg2, worker(3, Acc0), Acc2), +%% ?assertEqual(0, length(Acc3#acc.worker_uuids)), +%% check_quorum(Acc3, true), +%% +%% Expect = [ +%% {accepted, [{1, <<"foo1">>}, {1, <<"foo2">>}]}, +%% {accepted, [{2, <<"bar1">>}, {2, <<"bar2">>}]} +%% ], +%% Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc2), +%% ?assertEqual(Expect, Resps), +%% ?assertEqual(accepted, resp_health(Resps)) +%% end). +%% +%% +%% t_w2_exit1_ok() -> +%% ?_test(begin +%% Acc0 = create_init_acc(2), +%% Msg = {ok, [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}]}, +%% ExitMsg = {rexi_EXIT, blargh}, +%% +%% {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), +%% ?assertEqual(2, length(Acc1#acc.worker_uuids)), +%% check_quorum(Acc1, false), +%% +%% {ok, Acc2} = handle_message(ExitMsg, worker(2, Acc0), Acc1), +%% ?assertEqual(1, length(Acc2#acc.worker_uuids)), +%% check_quorum(Acc2, false), +%% +%% {stop, Acc3} = handle_message(Msg, worker(3, Acc0), Acc2), +%% ?assertEqual(0, length(Acc3#acc.worker_uuids)), +%% check_quorum(Acc3, true), +%% +%% Expect = [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}], +%% Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc3), +%% ?assertEqual(Expect, Resps), +%% ?assertEqual(ok, resp_health(Resps)) +%% end). +%% +%% +%% t_w2_exit2_accepted() -> +%% ?_test(begin +%% Acc0 = create_init_acc(2), +%% Msg = {ok, [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}]}, +%% ExitMsg = {rexi_EXIT, blargh}, +%% +%% {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), +%% ?assertEqual(2, length(Acc1#acc.worker_uuids)), +%% check_quorum(Acc1, false), +%% +%% {ok, Acc2} = handle_message(ExitMsg, worker(2, Acc0), Acc1), +%% ?assertEqual(1, length(Acc2#acc.worker_uuids)), +%% check_quorum(Acc2, false), +%% +%% {stop, Acc3} = handle_message(ExitMsg, worker(3, Acc0), Acc2), +%% ?assertEqual(0, length(Acc3#acc.worker_uuids)), +%% check_quorum(Acc3, true), +%% +%% Expect = [{accepted, [{1, <<"foo">>}]}, {accepted, [{2, <<"bar">>}]}], +%% Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc3), +%% ?assertEqual(Expect, Resps), +%% ?assertEqual(accepted, resp_health(Resps)) +%% end). +%% +%% +%% t_w2_exit3_error() -> +%% ?_test(begin +%% Acc0 = create_init_acc(2), +%% ExitMsg = {rexi_EXIT, blargh}, +%% +%% {ok, Acc1} = handle_message(ExitMsg, worker(1, Acc0), Acc0), +%% ?assertEqual(2, length(Acc1#acc.worker_uuids)), +%% check_quorum(Acc1, false), +%% +%% {ok, Acc2} = handle_message(ExitMsg, worker(2, Acc0), Acc1), +%% ?assertEqual(1, length(Acc2#acc.worker_uuids)), +%% check_quorum(Acc2, false), +%% +%% {stop, Acc3} = handle_message(ExitMsg, worker(3, Acc0), Acc2), +%% ?assertEqual(0, length(Acc3#acc.worker_uuids)), +%% check_quorum(Acc3, true), +%% +%% Expect = [ +%% {error, internal_server_error}, +%% {error, internal_server_error} +%% ], +%% Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc3), +%% ?assertEqual(Expect, Resps), +%% ?assertEqual(error, resp_health(Resps)) +%% end). +%% +%% +%% t_w4_accepted() -> +%% % Make sure we return when all workers have responded +%% % rather than wait around for a timeout if a user asks +%% % for a qourum with more than the available number of +%% % shards. +%% ?_test(begin +%% Acc0 = create_init_acc(4), +%% Msg = {ok, [{ok, [{1, <<"foo">>}]}, {ok, [{2, <<"bar">>}]}]}, +%% +%% {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), +%% ?assertEqual(2, length(Acc1#acc.worker_uuids)), +%% check_quorum(Acc1, false), +%% +%% {ok, Acc2} = handle_message(Msg, worker(2, Acc0), Acc1), +%% ?assertEqual(1, length(Acc2#acc.worker_uuids)), +%% check_quorum(Acc2, false), +%% +%% {stop, Acc3} = handle_message(Msg, worker(3, Acc0), Acc2), +%% ?assertEqual(0, length(Acc3#acc.worker_uuids)), +%% check_quorum(Acc3, true), +%% +%% Expect = [{accepted, [{1, <<"foo">>}]}, {accepted, [{2, <<"bar">>}]}], +%% Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc3), +%% ?assertEqual(Expect, Resps), +%% ?assertEqual(accepted, resp_health(Resps)) +%% end). +%% +%% +%% t_mixed_ok_accepted() -> +%% ?_test(begin +%% WorkerUUIDs = [ +%% {#shard{node = a, range = [1, 2]}, [<<"uuid1">>]}, +%% {#shard{node = b, range = [1, 2]}, [<<"uuid1">>]}, +%% {#shard{node = c, range = [1, 2]}, [<<"uuid1">>]}, +%% +%% {#shard{node = a, range = [3, 4]}, [<<"uuid2">>]}, +%% {#shard{node = b, range = [3, 4]}, [<<"uuid2">>]}, +%% {#shard{node = c, range = [3, 4]}, [<<"uuid2">>]} +%% ], +%% +%% Acc0 = #acc{ +%% worker_uuids = WorkerUUIDs, +%% resps = dict:from_list([{<<"uuid1">>, []}, {<<"uuid2">>, []}]), +%% uuid_counts = dict:from_list([{<<"uuid1">>, 3}, {<<"uuid2">>, 3}]), +%% w = 2 +%% }, +%% +%% Msg1 = {ok, [{ok, [{1, <<"foo">>}]}]}, +%% Msg2 = {ok, [{ok, [{2, <<"bar">>}]}]}, +%% ExitMsg = {rexi_EXIT, blargh}, +%% +%% {ok, Acc1} = handle_message(Msg1, worker(1, Acc0), Acc0), +%% {ok, Acc2} = handle_message(Msg1, worker(2, Acc0), Acc1), +%% {ok, Acc3} = handle_message(ExitMsg, worker(4, Acc0), Acc2), +%% {ok, Acc4} = handle_message(ExitMsg, worker(5, Acc0), Acc3), +%% {stop, Acc5} = handle_message(Msg2, worker(6, Acc0), Acc4), +%% +%% Expect = [{ok, [{1, <<"foo">>}]}, {accepted, [{2, <<"bar">>}]}], +%% Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc5), +%% ?assertEqual(Expect, Resps), +%% ?assertEqual(accepted, resp_health(Resps)) +%% end). +%% +%% +%% t_mixed_errors() -> +%% ?_test(begin +%% WorkerUUIDs = [ +%% {#shard{node = a, range = [1, 2]}, [<<"uuid1">>]}, +%% {#shard{node = b, range = [1, 2]}, [<<"uuid1">>]}, +%% {#shard{node = c, range = [1, 2]}, [<<"uuid1">>]}, +%% +%% {#shard{node = a, range = [3, 4]}, [<<"uuid2">>]}, +%% {#shard{node = b, range = [3, 4]}, [<<"uuid2">>]}, +%% {#shard{node = c, range = [3, 4]}, [<<"uuid2">>]} +%% ], +%% +%% Acc0 = #acc{ +%% worker_uuids = WorkerUUIDs, +%% resps = dict:from_list([{<<"uuid1">>, []}, {<<"uuid2">>, []}]), +%% uuid_counts = dict:from_list([{<<"uuid1">>, 3}, {<<"uuid2">>, 3}]), +%% w = 2 +%% }, +%% +%% Msg = {ok, [{ok, [{1, <<"foo">>}]}]}, +%% ExitMsg = {rexi_EXIT, blargh}, +%% +%% {ok, Acc1} = handle_message(Msg, worker(1, Acc0), Acc0), +%% {ok, Acc2} = handle_message(Msg, worker(2, Acc0), Acc1), +%% {ok, Acc3} = handle_message(ExitMsg, worker(4, Acc0), Acc2), +%% {ok, Acc4} = handle_message(ExitMsg, worker(5, Acc0), Acc3), +%% {stop, Acc5} = handle_message(ExitMsg, worker(6, Acc0), Acc4), +%% +%% Expect = [{ok, [{1, <<"foo">>}]}, {error, internal_server_error}], +%% Resps = format_resps([<<"uuid1">>, <<"uuid2">>], Acc5), +%% ?assertEqual(Expect, Resps), +%% ?assertEqual(error, resp_health(Resps)) +%% end). +%% +%% +%% create_init_acc(W) -> +%% UUID1 = <<"uuid1">>, +%% UUID2 = <<"uuid2">>, +%% +%% Nodes = [node1, node2, node3], +%% Shards = mem3_util:create_partition_map(<<"foo">>, 3, 1, Nodes), +%% +%% % Create our worker_uuids. We're relying on the fact that +%% % we're using a fake Q=1 db so we don't have to worry +%% % about any hashing here. +%% WorkerUUIDs = lists:map(fun(Shard) -> +%% {Shard#shard{ref = erlang:make_ref()}, [UUID1, UUID2]} +%% end, Shards), +%% +%% #acc{ +%% worker_uuids = WorkerUUIDs, +%% resps = dict:from_list([{UUID1, []}, {UUID2, []}]), +%% uuid_counts = dict:from_list([{UUID1, 3}, {UUID2, 3}]), +%% w = W +%% }. +%% +%% +%% worker(N, #acc{worker_uuids = WorkerUUIDs}) -> +%% {Worker, _} = lists:nth(N, WorkerUUIDs), +%% Worker. +%% +%% +%% check_quorum(Acc, Expect) -> +%% dict:fold(fun(_Shard, Resps, _) -> +%% ?assertEqual(Expect, has_quorum(Resps, 3, Acc#acc.w)) +%% end, nil, Acc#acc.resps). +%% +%% -endif. diff --git a/src/fabric/src/fabric_doc_update.erl b/src/fabric/src/fabric_doc_update.erl index c108c9a32..84f4bc47b 100644 --- a/src/fabric/src/fabric_doc_update.erl +++ b/src/fabric/src/fabric_doc_update.erl @@ -219,144 +219,144 @@ validate_atomic_update(_DbName, AllDocs, true) -> end, AllDocs), throw({aborted, PreCommitFailures}). -% eunits -doc_update1_test() -> - meck:new(couch_stats), - meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - meck:new(couch_log), - meck:expect(couch_log, warning, fun(_,_) -> ok end), - - Doc1 = #doc{revs = {1,[<<"foo">>]}}, - Doc2 = #doc{revs = {1,[<<"bar">>]}}, - Docs = [Doc1], - Docs2 = [Doc2, Doc1], - Dict = dict:from_list([{Doc,[]} || Doc <- Docs]), - Dict2 = dict:from_list([{Doc,[]} || Doc <- Docs2]), - - Shards = - mem3_util:create_partition_map("foo",3,1,["node1","node2","node3"]), - GroupedDocs = group_docs_by_shard_hack(<<"foo">>,Shards,Docs), - - - % test for W = 2 - AccW2 = {length(Shards), length(Docs), list_to_integer("2"), GroupedDocs, - Dict}, - - {ok,{WaitingCountW2_1,_,_,_,_}=AccW2_1} = - handle_message({ok, [{ok, Doc1}]},hd(Shards),AccW2), - ?assertEqual(WaitingCountW2_1,2), - {stop, FinalReplyW2 } = - handle_message({ok, [{ok, Doc1}]},lists:nth(2,Shards),AccW2_1), - ?assertEqual({ok, [{Doc1, {ok,Doc1}}]},FinalReplyW2), - - % test for W = 3 - AccW3 = {length(Shards), length(Docs), list_to_integer("3"), GroupedDocs, - Dict}, - - {ok,{WaitingCountW3_1,_,_,_,_}=AccW3_1} = - handle_message({ok, [{ok, Doc1}]},hd(Shards),AccW3), - ?assertEqual(WaitingCountW3_1,2), - - {ok,{WaitingCountW3_2,_,_,_,_}=AccW3_2} = - handle_message({ok, [{ok, Doc1}]},lists:nth(2,Shards),AccW3_1), - ?assertEqual(WaitingCountW3_2,1), - - {stop, FinalReplyW3 } = - handle_message({ok, [{ok, Doc1}]},lists:nth(3,Shards),AccW3_2), - ?assertEqual({ok, [{Doc1, {ok,Doc1}}]},FinalReplyW3), - - % test w quorum > # shards, which should fail immediately - - Shards2 = mem3_util:create_partition_map("foo",1,1,["node1"]), - GroupedDocs2 = group_docs_by_shard_hack(<<"foo">>,Shards2,Docs), - - AccW4 = - {length(Shards2), length(Docs), list_to_integer("2"), GroupedDocs2, Dict}, - Bool = - case handle_message({ok, [{ok, Doc1}]},hd(Shards2),AccW4) of - {stop, _Reply} -> - true; - _ -> false - end, - ?assertEqual(Bool,true), - - % Docs with no replies should end up as {error, internal_server_error} - SA1 = #shard{node=a, range=1}, - SB1 = #shard{node=b, range=1}, - SA2 = #shard{node=a, range=2}, - SB2 = #shard{node=b, range=2}, - GroupedDocs3 = [{SA1,[Doc1]}, {SB1,[Doc1]}, {SA2,[Doc2]}, {SB2,[Doc2]}], - StW5_0 = {length(GroupedDocs3), length(Docs2), 2, GroupedDocs3, Dict2}, - {ok, StW5_1} = handle_message({ok, [{ok, "A"}]}, SA1, StW5_0), - {ok, StW5_2} = handle_message({rexi_EXIT, nil}, SB1, StW5_1), - {ok, StW5_3} = handle_message({rexi_EXIT, nil}, SA2, StW5_2), - {stop, ReplyW5} = handle_message({rexi_EXIT, nil}, SB2, StW5_3), - ?assertEqual( - {error, [{Doc1,{accepted,"A"}},{Doc2,{error,internal_server_error}}]}, - ReplyW5 - ), - meck:unload(couch_log), - meck:unload(couch_stats). - - -doc_update2_test() -> - meck:new(couch_stats), - meck:expect(couch_stats, increment_counter, fun(_) -> ok end), - meck:new(couch_log), - meck:expect(couch_log, warning, fun(_,_) -> ok end), - - Doc1 = #doc{revs = {1,[<<"foo">>]}}, - Doc2 = #doc{revs = {1,[<<"bar">>]}}, - Docs = [Doc2, Doc1], - Shards = - mem3_util:create_partition_map("foo",3,1,["node1","node2","node3"]), - GroupedDocs = group_docs_by_shard_hack(<<"foo">>,Shards,Docs), - Acc0 = {length(Shards), length(Docs), list_to_integer("2"), GroupedDocs, - dict:from_list([{Doc,[]} || Doc <- Docs])}, - - {ok,{WaitingCount1,_,_,_,_}=Acc1} = - handle_message({ok, [{ok, Doc1},{ok, Doc2}]},hd(Shards),Acc0), - ?assertEqual(WaitingCount1,2), - - {ok,{WaitingCount2,_,_,_,_}=Acc2} = - handle_message({rexi_EXIT, 1},lists:nth(2,Shards),Acc1), - ?assertEqual(WaitingCount2,1), - - {stop, Reply} = - handle_message({rexi_EXIT, 1},lists:nth(3,Shards),Acc2), - - ?assertEqual({accepted, [{Doc1,{accepted,Doc2}}, {Doc2,{accepted,Doc1}}]}, - Reply), - meck:unload(couch_log), - meck:unload(couch_stats). - -doc_update3_test() -> - Doc1 = #doc{revs = {1,[<<"foo">>]}}, - Doc2 = #doc{revs = {1,[<<"bar">>]}}, - Docs = [Doc2, Doc1], - Shards = - mem3_util:create_partition_map("foo",3,1,["node1","node2","node3"]), - GroupedDocs = group_docs_by_shard_hack(<<"foo">>,Shards,Docs), - Acc0 = {length(Shards), length(Docs), list_to_integer("2"), GroupedDocs, - dict:from_list([{Doc,[]} || Doc <- Docs])}, - - {ok,{WaitingCount1,_,_,_,_}=Acc1} = - handle_message({ok, [{ok, Doc1},{ok, Doc2}]},hd(Shards),Acc0), - ?assertEqual(WaitingCount1,2), - - {ok,{WaitingCount2,_,_,_,_}=Acc2} = - handle_message({rexi_EXIT, 1},lists:nth(2,Shards),Acc1), - ?assertEqual(WaitingCount2,1), - - {stop, Reply} = - handle_message({ok, [{ok, Doc1},{ok, Doc2}]},lists:nth(3,Shards),Acc2), - - ?assertEqual({ok, [{Doc1, {ok, Doc2}},{Doc2, {ok,Doc1}}]},Reply). - -% needed for testing to avoid having to start the mem3 application -group_docs_by_shard_hack(_DbName, Shards, Docs) -> - dict:to_list(lists:foldl(fun(#doc{id=_Id} = Doc, D0) -> - lists:foldl(fun(Shard, D1) -> - dict:append(Shard, Doc, D1) - end, D0, Shards) - end, dict:new(), Docs)). +%% % eunits +%% doc_update1_test() -> +%% meck:new(couch_stats), +%% meck:expect(couch_stats, increment_counter, fun(_) -> ok end), +%% meck:new(couch_log), +%% meck:expect(couch_log, warning, fun(_,_) -> ok end), +%% +%% Doc1 = #doc{revs = {1,[<<"foo">>]}}, +%% Doc2 = #doc{revs = {1,[<<"bar">>]}}, +%% Docs = [Doc1], +%% Docs2 = [Doc2, Doc1], +%% Dict = dict:from_list([{Doc,[]} || Doc <- Docs]), +%% Dict2 = dict:from_list([{Doc,[]} || Doc <- Docs2]), +%% +%% Shards = +%% mem3_util:create_partition_map("foo",3,1,["node1","node2","node3"]), +%% GroupedDocs = group_docs_by_shard_hack(<<"foo">>,Shards,Docs), +%% +%% +%% % test for W = 2 +%% AccW2 = {length(Shards), length(Docs), list_to_integer("2"), GroupedDocs, +%% Dict}, +%% +%% {ok,{WaitingCountW2_1,_,_,_,_}=AccW2_1} = +%% handle_message({ok, [{ok, Doc1}]},hd(Shards),AccW2), +%% ?assertEqual(WaitingCountW2_1,2), +%% {stop, FinalReplyW2 } = +%% handle_message({ok, [{ok, Doc1}]},lists:nth(2,Shards),AccW2_1), +%% ?assertEqual({ok, [{Doc1, {ok,Doc1}}]},FinalReplyW2), +%% +%% % test for W = 3 +%% AccW3 = {length(Shards), length(Docs), list_to_integer("3"), GroupedDocs, +%% Dict}, +%% +%% {ok,{WaitingCountW3_1,_,_,_,_}=AccW3_1} = +%% handle_message({ok, [{ok, Doc1}]},hd(Shards),AccW3), +%% ?assertEqual(WaitingCountW3_1,2), +%% +%% {ok,{WaitingCountW3_2,_,_,_,_}=AccW3_2} = +%% handle_message({ok, [{ok, Doc1}]},lists:nth(2,Shards),AccW3_1), +%% ?assertEqual(WaitingCountW3_2,1), +%% +%% {stop, FinalReplyW3 } = +%% handle_message({ok, [{ok, Doc1}]},lists:nth(3,Shards),AccW3_2), +%% ?assertEqual({ok, [{Doc1, {ok,Doc1}}]},FinalReplyW3), +%% +%% % test w quorum > # shards, which should fail immediately +%% +%% Shards2 = mem3_util:create_partition_map("foo",1,1,["node1"]), +%% GroupedDocs2 = group_docs_by_shard_hack(<<"foo">>,Shards2,Docs), +%% +%% AccW4 = +%% {length(Shards2), length(Docs), list_to_integer("2"), GroupedDocs2, Dict}, +%% Bool = +%% case handle_message({ok, [{ok, Doc1}]},hd(Shards2),AccW4) of +%% {stop, _Reply} -> +%% true; +%% _ -> false +%% end, +%% ?assertEqual(Bool,true), +%% +%% % Docs with no replies should end up as {error, internal_server_error} +%% SA1 = #shard{node=a, range=1}, +%% SB1 = #shard{node=b, range=1}, +%% SA2 = #shard{node=a, range=2}, +%% SB2 = #shard{node=b, range=2}, +%% GroupedDocs3 = [{SA1,[Doc1]}, {SB1,[Doc1]}, {SA2,[Doc2]}, {SB2,[Doc2]}], +%% StW5_0 = {length(GroupedDocs3), length(Docs2), 2, GroupedDocs3, Dict2}, +%% {ok, StW5_1} = handle_message({ok, [{ok, "A"}]}, SA1, StW5_0), +%% {ok, StW5_2} = handle_message({rexi_EXIT, nil}, SB1, StW5_1), +%% {ok, StW5_3} = handle_message({rexi_EXIT, nil}, SA2, StW5_2), +%% {stop, ReplyW5} = handle_message({rexi_EXIT, nil}, SB2, StW5_3), +%% ?assertEqual( +%% {error, [{Doc1,{accepted,"A"}},{Doc2,{error,internal_server_error}}]}, +%% ReplyW5 +%% ), +%% meck:unload(couch_log), +%% meck:unload(couch_stats). +%% +%% +%% doc_update2_test() -> +%% meck:new(couch_stats), +%% meck:expect(couch_stats, increment_counter, fun(_) -> ok end), +%% meck:new(couch_log), +%% meck:expect(couch_log, warning, fun(_,_) -> ok end), +%% +%% Doc1 = #doc{revs = {1,[<<"foo">>]}}, +%% Doc2 = #doc{revs = {1,[<<"bar">>]}}, +%% Docs = [Doc2, Doc1], +%% Shards = +%% mem3_util:create_partition_map("foo",3,1,["node1","node2","node3"]), +%% GroupedDocs = group_docs_by_shard_hack(<<"foo">>,Shards,Docs), +%% Acc0 = {length(Shards), length(Docs), list_to_integer("2"), GroupedDocs, +%% dict:from_list([{Doc,[]} || Doc <- Docs])}, +%% +%% {ok,{WaitingCount1,_,_,_,_}=Acc1} = +%% handle_message({ok, [{ok, Doc1},{ok, Doc2}]},hd(Shards),Acc0), +%% ?assertEqual(WaitingCount1,2), +%% +%% {ok,{WaitingCount2,_,_,_,_}=Acc2} = +%% handle_message({rexi_EXIT, 1},lists:nth(2,Shards),Acc1), +%% ?assertEqual(WaitingCount2,1), +%% +%% {stop, Reply} = +%% handle_message({rexi_EXIT, 1},lists:nth(3,Shards),Acc2), +%% +%% ?assertEqual({accepted, [{Doc1,{accepted,Doc2}}, {Doc2,{accepted,Doc1}}]}, +%% Reply), +%% meck:unload(couch_log), +%% meck:unload(couch_stats). +%% +%% doc_update3_test() -> +%% Doc1 = #doc{revs = {1,[<<"foo">>]}}, +%% Doc2 = #doc{revs = {1,[<<"bar">>]}}, +%% Docs = [Doc2, Doc1], +%% Shards = +%% mem3_util:create_partition_map("foo",3,1,["node1","node2","node3"]), +%% GroupedDocs = group_docs_by_shard_hack(<<"foo">>,Shards,Docs), +%% Acc0 = {length(Shards), length(Docs), list_to_integer("2"), GroupedDocs, +%% dict:from_list([{Doc,[]} || Doc <- Docs])}, +%% +%% {ok,{WaitingCount1,_,_,_,_}=Acc1} = +%% handle_message({ok, [{ok, Doc1},{ok, Doc2}]},hd(Shards),Acc0), +%% ?assertEqual(WaitingCount1,2), +%% +%% {ok,{WaitingCount2,_,_,_,_}=Acc2} = +%% handle_message({rexi_EXIT, 1},lists:nth(2,Shards),Acc1), +%% ?assertEqual(WaitingCount2,1), +%% +%% {stop, Reply} = +%% handle_message({ok, [{ok, Doc1},{ok, Doc2}]},lists:nth(3,Shards),Acc2), +%% +%% ?assertEqual({ok, [{Doc1, {ok, Doc2}},{Doc2, {ok,Doc1}}]},Reply). +%% +%% % needed for testing to avoid having to start the mem3 application +%% group_docs_by_shard_hack(_DbName, Shards, Docs) -> +%% dict:to_list(lists:foldl(fun(#doc{id=_Id} = Doc, D0) -> +%% lists:foldl(fun(Shard, D1) -> +%% dict:append(Shard, Doc, D1) +%% end, D0, Shards) +%% end, dict:new(), Docs)). diff --git a/src/fabric/src/fabric_rpc.erl b/src/fabric/src/fabric_rpc.erl index 97374be1f..212a1da57 100644 --- a/src/fabric/src/fabric_rpc.erl +++ b/src/fabric/src/fabric_rpc.erl @@ -643,22 +643,22 @@ uuid(Db) -> uuid_prefix_len() -> list_to_integer(config:get("fabric", "uuid_prefix_len", "7")). --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -maybe_filtered_json_doc_no_filter_test() -> - Body = {[{<<"a">>, 1}]}, - Doc = #doc{id = <<"1">>, revs = {1, [<<"r1">>]}, body = Body}, - {JDocProps} = maybe_filtered_json_doc(Doc, [], x), - ExpectedProps = [{<<"_id">>, <<"1">>}, {<<"_rev">>, <<"1-r1">>}, {<<"a">>, 1}], - ?assertEqual(lists:keysort(1, JDocProps), ExpectedProps). - -maybe_filtered_json_doc_with_filter_test() -> - Body = {[{<<"a">>, 1}]}, - Doc = #doc{id = <<"1">>, revs = {1, [<<"r1">>]}, body = Body}, - Fields = [<<"a">>, <<"nonexistent">>], - Filter = {selector, main_only, {some_selector, Fields}}, - {JDocProps} = maybe_filtered_json_doc(Doc, [], Filter), - ?assertEqual(JDocProps, [{<<"a">>, 1}]). - --endif. +%% -ifdef(TEST). +%% -include_lib("eunit/include/eunit.hrl"). +%% +%% maybe_filtered_json_doc_no_filter_test() -> +%% Body = {[{<<"a">>, 1}]}, +%% Doc = #doc{id = <<"1">>, revs = {1, [<<"r1">>]}, body = Body}, +%% {JDocProps} = maybe_filtered_json_doc(Doc, [], x), +%% ExpectedProps = [{<<"_id">>, <<"1">>}, {<<"_rev">>, <<"1-r1">>}, {<<"a">>, 1}], +%% ?assertEqual(lists:keysort(1, JDocProps), ExpectedProps). +%% +%% maybe_filtered_json_doc_with_filter_test() -> +%% Body = {[{<<"a">>, 1}]}, +%% Doc = #doc{id = <<"1">>, revs = {1, [<<"r1">>]}, body = Body}, +%% Fields = [<<"a">>, <<"nonexistent">>], +%% Filter = {selector, main_only, {some_selector, Fields}}, +%% {JDocProps} = maybe_filtered_json_doc(Doc, [], Filter), +%% ?assertEqual(JDocProps, [{<<"a">>, 1}]). +%% +%% -endif. diff --git a/src/fabric/src/fabric_streams.erl b/src/fabric/src/fabric_streams.erl index 288c67cab..b1fc11bd3 100644 --- a/src/fabric/src/fabric_streams.erl +++ b/src/fabric/src/fabric_streams.erl @@ -169,82 +169,82 @@ add_worker_to_cleaner(CoordinatorPid, Worker) -> --ifdef(TEST). - --include_lib("eunit/include/eunit.hrl"). - -worker_cleaner_test_() -> - { - "Fabric spawn_worker_cleaner test", { - setup, fun setup/0, fun teardown/1, - fun(_) -> [ - should_clean_workers(), - does_not_fire_if_cleanup_called(), - should_clean_additional_worker_too() - ] end - } - }. - - -should_clean_workers() -> - ?_test(begin - meck:reset(rexi), - erase(?WORKER_CLEANER), - Workers = [ - #shard{node = 'n1', ref = make_ref()}, - #shard{node = 'n2', ref = make_ref()} - ], - {Coord, _} = spawn_monitor(fun() -> receive die -> ok end end), - Cleaner = spawn_worker_cleaner(Coord, Workers), - Ref = erlang:monitor(process, Cleaner), - Coord ! die, - receive {'DOWN', Ref, _, Cleaner, _} -> ok end, - ?assertEqual(2, meck:num_calls(rexi, kill, 2)) - end). - - -does_not_fire_if_cleanup_called() -> - ?_test(begin - meck:reset(rexi), - erase(?WORKER_CLEANER), - Workers = [ - #shard{node = 'n1', ref = make_ref()}, - #shard{node = 'n2', ref = make_ref()} - ], - {Coord, _} = spawn_monitor(fun() -> receive die -> ok end end), - Cleaner = spawn_worker_cleaner(Coord, Workers), - Ref = erlang:monitor(process, Cleaner), - cleanup(Workers), - Coord ! die, - receive {'DOWN', Ref, _, _, _} -> ok end, - % 2 calls would be from cleanup/1 function. If cleanup process fired - % too it would have been 4 calls total. - ?assertEqual(2, meck:num_calls(rexi, kill, 2)) - end). - - -should_clean_additional_worker_too() -> - ?_test(begin - meck:reset(rexi), - erase(?WORKER_CLEANER), - Workers = [ - #shard{node = 'n1', ref = make_ref()} - ], - {Coord, _} = spawn_monitor(fun() -> receive die -> ok end end), - Cleaner = spawn_worker_cleaner(Coord, Workers), - add_worker_to_cleaner(Coord, #shard{node = 'n2', ref = make_ref()}), - Ref = erlang:monitor(process, Cleaner), - Coord ! die, - receive {'DOWN', Ref, _, Cleaner, _} -> ok end, - ?assertEqual(2, meck:num_calls(rexi, kill, 2)) - end). - - -setup() -> - ok = meck:expect(rexi, kill, fun(_, _) -> ok end). - - -teardown(_) -> - meck:unload(). - --endif. +%% -ifdef(TEST). +%% +%% -include_lib("eunit/include/eunit.hrl"). +%% +%% worker_cleaner_test_() -> +%% { +%% "Fabric spawn_worker_cleaner test", { +%% setup, fun setup/0, fun teardown/1, +%% fun(_) -> [ +%% should_clean_workers(), +%% does_not_fire_if_cleanup_called(), +%% should_clean_additional_worker_too() +%% ] end +%% } +%% }. +%% +%% +%% should_clean_workers() -> +%% ?_test(begin +%% meck:reset(rexi), +%% erase(?WORKER_CLEANER), +%% Workers = [ +%% #shard{node = 'n1', ref = make_ref()}, +%% #shard{node = 'n2', ref = make_ref()} +%% ], +%% {Coord, _} = spawn_monitor(fun() -> receive die -> ok end end), +%% Cleaner = spawn_worker_cleaner(Coord, Workers), +%% Ref = erlang:monitor(process, Cleaner), +%% Coord ! die, +%% receive {'DOWN', Ref, _, Cleaner, _} -> ok end, +%% ?assertEqual(2, meck:num_calls(rexi, kill, 2)) +%% end). +%% +%% +%% does_not_fire_if_cleanup_called() -> +%% ?_test(begin +%% meck:reset(rexi), +%% erase(?WORKER_CLEANER), +%% Workers = [ +%% #shard{node = 'n1', ref = make_ref()}, +%% #shard{node = 'n2', ref = make_ref()} +%% ], +%% {Coord, _} = spawn_monitor(fun() -> receive die -> ok end end), +%% Cleaner = spawn_worker_cleaner(Coord, Workers), +%% Ref = erlang:monitor(process, Cleaner), +%% cleanup(Workers), +%% Coord ! die, +%% receive {'DOWN', Ref, _, _, _} -> ok end, +%% % 2 calls would be from cleanup/1 function. If cleanup process fired +%% % too it would have been 4 calls total. +%% ?assertEqual(2, meck:num_calls(rexi, kill, 2)) +%% end). +%% +%% +%% should_clean_additional_worker_too() -> +%% ?_test(begin +%% meck:reset(rexi), +%% erase(?WORKER_CLEANER), +%% Workers = [ +%% #shard{node = 'n1', ref = make_ref()} +%% ], +%% {Coord, _} = spawn_monitor(fun() -> receive die -> ok end end), +%% Cleaner = spawn_worker_cleaner(Coord, Workers), +%% add_worker_to_cleaner(Coord, #shard{node = 'n2', ref = make_ref()}), +%% Ref = erlang:monitor(process, Cleaner), +%% Coord ! die, +%% receive {'DOWN', Ref, _, Cleaner, _} -> ok end, +%% ?assertEqual(2, meck:num_calls(rexi, kill, 2)) +%% end). +%% +%% +%% setup() -> +%% ok = meck:expect(rexi, kill, fun(_, _) -> ok end). +%% +%% +%% teardown(_) -> +%% meck:unload(). +%% +%% -endif. diff --git a/src/fabric/src/fabric_util.erl b/src/fabric/src/fabric_util.erl index 1ba1d17ea..94fac3431 100644 --- a/src/fabric/src/fabric_util.erl +++ b/src/fabric/src/fabric_util.erl @@ -188,30 +188,30 @@ create_monitors(Shards) -> ]), rexi_monitor:start(MonRefs). -%% verify only id and rev are used in key. -update_counter_test() -> - Reply = {ok, #doc{id = <<"id">>, revs = <<"rev">>, - body = <<"body">>, atts = <<"atts">>}}, - ?assertEqual([{{<<"id">>,<<"rev">>}, {Reply, 1}}], - update_counter(Reply, 1, [])). - -remove_ancestors_test() -> - Foo1 = {ok, #doc{revs = {1, [<<"foo">>]}}}, - Foo2 = {ok, #doc{revs = {2, [<<"foo2">>, <<"foo">>]}}}, - Bar1 = {ok, #doc{revs = {1, [<<"bar">>]}}}, - Bar2 = {not_found, {1,<<"bar">>}}, - ?assertEqual( - [kv(Bar1,1), kv(Foo1,1)], - remove_ancestors([kv(Bar1,1), kv(Foo1,1)], []) - ), - ?assertEqual( - [kv(Bar1,1), kv(Foo2,2)], - remove_ancestors([kv(Bar1,1), kv(Foo1,1), kv(Foo2,1)], []) - ), - ?assertEqual( - [kv(Bar1,2)], - remove_ancestors([kv(Bar2,1), kv(Bar1,1)], []) - ). +%% %% verify only id and rev are used in key. +%% update_counter_test() -> +%% Reply = {ok, #doc{id = <<"id">>, revs = <<"rev">>, +%% body = <<"body">>, atts = <<"atts">>}}, +%% ?assertEqual([{{<<"id">>,<<"rev">>}, {Reply, 1}}], +%% update_counter(Reply, 1, [])). +%% +%% remove_ancestors_test() -> +%% Foo1 = {ok, #doc{revs = {1, [<<"foo">>]}}}, +%% Foo2 = {ok, #doc{revs = {2, [<<"foo2">>, <<"foo">>]}}}, +%% Bar1 = {ok, #doc{revs = {1, [<<"bar">>]}}}, +%% Bar2 = {not_found, {1,<<"bar">>}}, +%% ?assertEqual( +%% [kv(Bar1,1), kv(Foo1,1)], +%% remove_ancestors([kv(Bar1,1), kv(Foo1,1)], []) +%% ), +%% ?assertEqual( +%% [kv(Bar1,1), kv(Foo2,2)], +%% remove_ancestors([kv(Bar1,1), kv(Foo1,1), kv(Foo2,1)], []) +%% ), +%% ?assertEqual( +%% [kv(Bar1,2)], +%% remove_ancestors([kv(Bar2,1), kv(Bar1,1)], []) +%% ). is_replicator_db(DbName) -> path_ends_with(DbName, <<"_replicator">>). diff --git a/src/fabric/src/fabric_view.erl b/src/fabric/src/fabric_view.erl index 27b0c275f..88a88325f 100644 --- a/src/fabric/src/fabric_view.erl +++ b/src/fabric/src/fabric_view.erl @@ -391,55 +391,55 @@ fix_skip_and_limit(#mrargs{} = Args) -> remove_finalizer(Args) -> couch_mrview_util:set_extra(Args, finalizer, null). -% unit test -is_progress_possible_test() -> - EndPoint = 2 bsl 31, - T1 = [[0, EndPoint-1]], - ?assertEqual(is_progress_possible(mk_cnts(T1)),true), - T2 = [[0,10],[11,20],[21,EndPoint-1]], - ?assertEqual(is_progress_possible(mk_cnts(T2)),true), - % gap - T3 = [[0,10],[12,EndPoint-1]], - ?assertEqual(is_progress_possible(mk_cnts(T3)),false), - % outside range - T4 = [[1,10],[11,20],[21,EndPoint-1]], - ?assertEqual(is_progress_possible(mk_cnts(T4)),false), - % outside range - T5 = [[0,10],[11,20],[21,EndPoint]], - ?assertEqual(is_progress_possible(mk_cnts(T5)),false). - -remove_overlapping_shards_test() -> - meck:new(rexi), - meck:expect(rexi, kill, fun(_, _) -> ok end), - EndPoint = 2 bsl 31, - T1 = [[0,10],[11,20],[21,EndPoint-1]], - Shards = mk_cnts(T1,3), - ?assertEqual(orddict:size( - remove_overlapping_shards(#shard{name=list_to_atom("node-3"), - node=list_to_atom("node-3"), - range=[11,20]}, - Shards)),7), - meck:unload(rexi). - -mk_cnts(Ranges) -> - Shards = lists:map(fun(Range) -> - #shard{range=Range} - end, - Ranges), - orddict:from_list([{Shard,nil} || Shard <- Shards]). - -mk_cnts(Ranges, NoNodes) -> - orddict:from_list([{Shard,nil} - || Shard <- - lists:flatten(lists:map( - fun(Range) -> - mk_shards(NoNodes,Range,[]) - end, Ranges))] - ). - -mk_shards(0,_Range,Shards) -> - Shards; -mk_shards(NoNodes,Range,Shards) -> - NodeName = list_to_atom("node-" ++ integer_to_list(NoNodes)), - mk_shards(NoNodes-1,Range, - [#shard{name=NodeName, node=NodeName, range=Range} | Shards]). +%% % unit test +%% is_progress_possible_test() -> +%% EndPoint = 2 bsl 31, +%% T1 = [[0, EndPoint-1]], +%% ?assertEqual(is_progress_possible(mk_cnts(T1)),true), +%% T2 = [[0,10],[11,20],[21,EndPoint-1]], +%% ?assertEqual(is_progress_possible(mk_cnts(T2)),true), +%% % gap +%% T3 = [[0,10],[12,EndPoint-1]], +%% ?assertEqual(is_progress_possible(mk_cnts(T3)),false), +%% % outside range +%% T4 = [[1,10],[11,20],[21,EndPoint-1]], +%% ?assertEqual(is_progress_possible(mk_cnts(T4)),false), +%% % outside range +%% T5 = [[0,10],[11,20],[21,EndPoint]], +%% ?assertEqual(is_progress_possible(mk_cnts(T5)),false). +%% +%% remove_overlapping_shards_test() -> +%% meck:new(rexi), +%% meck:expect(rexi, kill, fun(_, _) -> ok end), +%% EndPoint = 2 bsl 31, +%% T1 = [[0,10],[11,20],[21,EndPoint-1]], +%% Shards = mk_cnts(T1,3), +%% ?assertEqual(orddict:size( +%% remove_overlapping_shards(#shard{name=list_to_atom("node-3"), +%% node=list_to_atom("node-3"), +%% range=[11,20]}, +%% Shards)),7), +%% meck:unload(rexi). +%% +%% mk_cnts(Ranges) -> +%% Shards = lists:map(fun(Range) -> +%% #shard{range=Range} +%% end, +%% Ranges), +%% orddict:from_list([{Shard,nil} || Shard <- Shards]). +%% +%% mk_cnts(Ranges, NoNodes) -> +%% orddict:from_list([{Shard,nil} +%% || Shard <- +%% lists:flatten(lists:map( +%% fun(Range) -> +%% mk_shards(NoNodes,Range,[]) +%% end, Ranges))] +%% ). +%% +%% mk_shards(0,_Range,Shards) -> +%% Shards; +%% mk_shards(NoNodes,Range,Shards) -> +%% NodeName = list_to_atom("node-" ++ integer_to_list(NoNodes)), +%% mk_shards(NoNodes-1,Range, +%% [#shard{name=NodeName, node=NodeName, range=Range} | Shards]). diff --git a/src/fabric/src/fabric_view_changes.erl b/src/fabric/src/fabric_view_changes.erl index f96bb058d..ff81f3175 100644 --- a/src/fabric/src/fabric_view_changes.erl +++ b/src/fabric/src/fabric_view_changes.erl @@ -520,51 +520,51 @@ get_changes_epoch() -> increment_changes_epoch() -> application:set_env(fabric, changes_epoch, os:timestamp()). -unpack_seqs_test() -> - meck:new(mem3), - meck:new(fabric_view), - meck:expect(mem3, get_shard, fun(_, _, _) -> {ok, #shard{}} end), - meck:expect(fabric_view, is_progress_possible, fun(_) -> true end), - - % BigCouch 0.3 style. - assert_shards("23423-g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" - "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" - "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA"), - - % BigCouch 0.4 style. - assert_shards([23423,<<"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" - "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" - "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA">>]), - - % BigCouch 0.4 style (as string). - assert_shards("[23423,\"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" - "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" - "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA\"]"), - assert_shards("[23423 ,\"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" - "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" - "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA\"]"), - assert_shards("[23423, \"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" - "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" - "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA\"]"), - assert_shards("[23423 , \"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" - "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" - "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA\"]"), - - % with internal hypen - assert_shards("651-g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwNDLXMwBCwxygOFMiQ" - "5L8____sxJTcalIUgCSSfZgReE4FTmAFMWDFYXgVJQAUlQPVuSKS1EeC5BkaABSQHXz8" - "VgJUbgAonB_VqIPfoUHIArvE7T6AUQh0I1-WQAzp1XB"), - assert_shards([651,"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwNDLXMwBCwxygOFMiQ" - "5L8____sxJTcalIUgCSSfZgReE4FTmAFMWDFYXgVJQAUlQPVuSKS1EeC5BkaABSQHXz8" - "VgJUbgAonB_VqIPfoUHIArvE7T6AUQh0I1-WQAzp1XB"]), - - % CouchDB 1.2 style - assert_shards("\"23423-g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" - "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" - "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA\""), - - meck:unload(fabric_view), - meck:unload(mem3). - -assert_shards(Packed) -> - ?assertMatch([{#shard{},_}|_], unpack_seqs(Packed, <<"foo">>)). +%% unpack_seqs_test() -> +%% meck:new(mem3), +%% meck:new(fabric_view), +%% meck:expect(mem3, get_shard, fun(_, _, _) -> {ok, #shard{}} end), +%% meck:expect(fabric_view, is_progress_possible, fun(_) -> true end), +%% +%% % BigCouch 0.3 style. +%% assert_shards("23423-g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" +%% "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" +%% "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA"), +%% +%% % BigCouch 0.4 style. +%% assert_shards([23423,<<"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" +%% "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" +%% "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA">>]), +%% +%% % BigCouch 0.4 style (as string). +%% assert_shards("[23423,\"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" +%% "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" +%% "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA\"]"), +%% assert_shards("[23423 ,\"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" +%% "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" +%% "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA\"]"), +%% assert_shards("[23423, \"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" +%% "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" +%% "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA\"]"), +%% assert_shards("[23423 , \"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" +%% "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" +%% "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA\"]"), +%% +%% % with internal hypen +%% assert_shards("651-g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwNDLXMwBCwxygOFMiQ" +%% "5L8____sxJTcalIUgCSSfZgReE4FTmAFMWDFYXgVJQAUlQPVuSKS1EeC5BkaABSQHXz8" +%% "VgJUbgAonB_VqIPfoUHIArvE7T6AUQh0I1-WQAzp1XB"), +%% assert_shards([651,"g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwNDLXMwBCwxygOFMiQ" +%% "5L8____sxJTcalIUgCSSfZgReE4FTmAFMWDFYXgVJQAUlQPVuSKS1EeC5BkaABSQHXz8" +%% "VgJUbgAonB_VqIPfoUHIArvE7T6AUQh0I1-WQAzp1XB"]), +%% +%% % CouchDB 1.2 style +%% assert_shards("\"23423-g1AAAAE7eJzLYWBg4MhgTmHgS0ktM3QwND" +%% "LXMwBCwxygOFMiQ5L8____sxIZcKlIUgCSSfZgRUw4FTmAFMWDFTHiVJQAUlSPX1Ee" +%% "C5BkaABSQHXzsxKZ8StcAFG4H4_bIAoPQBTeJ2j1A4hCUJBkAQC7U1NA\""), +%% +%% meck:unload(fabric_view), +%% meck:unload(mem3). +%% +%% assert_shards(Packed) -> +%% ?assertMatch([{#shard{},_}|_], unpack_seqs(Packed, <<"foo">>)). diff --git a/src/fabric/test/fabric_rpc_purge_tests.erl b/src/fabric/test/fabric_rpc_purge_tests.erl deleted file mode 100644 index 4eafb2bc4..000000000 --- a/src/fabric/test/fabric_rpc_purge_tests.erl +++ /dev/null @@ -1,307 +0,0 @@ -% 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(fabric_rpc_purge_tests). - - --include_lib("couch/include/couch_eunit.hrl"). --include_lib("couch/include/couch_db.hrl"). - - --define(TDEF(A), {A, fun A/1}). - -% TODO: Add tests: -% - filter some updates -% - allow for an update that was filtered by a node -% - ignore lagging nodes - -main_test_() -> - { - setup, - spawn, - fun setup_all/0, - fun teardown_all/1, - [ - { - foreach, - fun setup_no_purge/0, - fun teardown_no_purge/1, - lists:map(fun wrap/1, [ - ?TDEF(t_no_purge_no_filter) - ]) - }, - { - foreach, - fun setup_single_purge/0, - fun teardown_single_purge/1, - lists:map(fun wrap/1, [ - ?TDEF(t_filter), - ?TDEF(t_filter_unknown_node), - ?TDEF(t_filter_local_node), - ?TDEF(t_no_filter_old_node), - ?TDEF(t_no_filter_different_node), - ?TDEF(t_no_filter_after_repl) - ]) - }, - { - foreach, - fun setup_multi_purge/0, - fun teardown_multi_purge/1, - lists:map(fun wrap/1, [ - ?TDEF(t_filter), - ?TDEF(t_filter_unknown_node), - ?TDEF(t_filter_local_node), - ?TDEF(t_no_filter_old_node), - ?TDEF(t_no_filter_different_node), - ?TDEF(t_no_filter_after_repl) - ]) - } - ] - }. - - -setup_all() -> - test_util:start_couch(). - - -teardown_all(Ctx) -> - test_util:stop_couch(Ctx). - - -setup_no_purge() -> - {ok, Db} = create_db(), - populate_db(Db), - couch_db:name(Db). - - -teardown_no_purge(DbName) -> - ok = couch_server:delete(DbName, []). - - -setup_single_purge() -> - DbName = setup_no_purge(), - DocId = <<"0003">>, - {ok, OldDoc} = open_doc(DbName, DocId), - purge_doc(DbName, DocId), - {DbName, DocId, OldDoc, 1}. - - -teardown_single_purge({DbName, _, _, _}) -> - teardown_no_purge(DbName). - - -setup_multi_purge() -> - DbName = setup_no_purge(), - DocId = <<"0003">>, - {ok, OldDoc} = open_doc(DbName, DocId), - lists:foreach(fun(I) -> - PDocId = iolist_to_binary(io_lib:format("~4..0b", [I])), - purge_doc(DbName, PDocId) - end, lists:seq(1, 5)), - {DbName, DocId, OldDoc, 3}. - - -teardown_multi_purge(Ctx) -> - teardown_single_purge(Ctx). - - -t_no_purge_no_filter(DbName) -> - DocId = <<"0003">>, - - {ok, OldDoc} = open_doc(DbName, DocId), - NewDoc = create_update(OldDoc, 2), - - rpc_update_doc(DbName, NewDoc), - - {ok, CurrDoc} = open_doc(DbName, DocId), - ?assert(CurrDoc /= OldDoc), - ?assert(CurrDoc == NewDoc). - - -t_filter({DbName, DocId, OldDoc, _PSeq}) -> - ?assertEqual({not_found, missing}, open_doc(DbName, DocId)), - create_purge_checkpoint(DbName, 0), - - rpc_update_doc(DbName, OldDoc), - - ?assertEqual({not_found, missing}, open_doc(DbName, DocId)). - - -t_filter_unknown_node({DbName, DocId, OldDoc, _PSeq}) -> - % Unknown nodes are assumed to start at PurgeSeq = 0 - ?assertEqual({not_found, missing}, open_doc(DbName, DocId)), - create_purge_checkpoint(DbName, 0), - - {Pos, [Rev | _]} = OldDoc#doc.revs, - RROpt = {read_repair, [{'blargh@127.0.0.1', [{Pos, Rev}]}]}, - rpc_update_doc(DbName, OldDoc, [RROpt]), - - ?assertEqual({not_found, missing}, open_doc(DbName, DocId)). - - -t_no_filter_old_node({DbName, DocId, OldDoc, PSeq}) -> - ?assertEqual({not_found, missing}, open_doc(DbName, DocId)), - create_purge_checkpoint(DbName, PSeq), - - % The random UUID is to generate a badarg exception when - % we try and convert it to an existing atom. - create_purge_checkpoint(DbName, 0, couch_uuids:random()), - - rpc_update_doc(DbName, OldDoc), - - ?assertEqual({ok, OldDoc}, open_doc(DbName, DocId)). - - -t_no_filter_different_node({DbName, DocId, OldDoc, PSeq}) -> - ?assertEqual({not_found, missing}, open_doc(DbName, DocId)), - create_purge_checkpoint(DbName, PSeq), - - % Create a valid purge for a different node - TgtNode = list_to_binary(atom_to_list('notfoo@127.0.0.1')), - create_purge_checkpoint(DbName, 0, TgtNode), - - rpc_update_doc(DbName, OldDoc), - - ?assertEqual({ok, OldDoc}, open_doc(DbName, DocId)). - - -t_filter_local_node({DbName, DocId, OldDoc, PSeq}) -> - ?assertEqual({not_found, missing}, open_doc(DbName, DocId)), - create_purge_checkpoint(DbName, PSeq), - - % Create a valid purge for a different node - TgtNode = list_to_binary(atom_to_list('notfoo@127.0.0.1')), - create_purge_checkpoint(DbName, 0, TgtNode), - - % Add a local node rev to the list of node revs. It should - % be filtered out - {Pos, [Rev | _]} = OldDoc#doc.revs, - RROpts = [{read_repair, [ - {tgt_node(), [{Pos, Rev}]}, - {node(), [{1, <<"123">>}]} - ]}], - rpc_update_doc(DbName, OldDoc, RROpts), - - ?assertEqual({ok, OldDoc}, open_doc(DbName, DocId)). - - -t_no_filter_after_repl({DbName, DocId, OldDoc, PSeq}) -> - ?assertEqual({not_found, missing}, open_doc(DbName, DocId)), - create_purge_checkpoint(DbName, PSeq), - - rpc_update_doc(DbName, OldDoc), - - ?assertEqual({ok, OldDoc}, open_doc(DbName, DocId)). - - -wrap({Name, Fun}) -> - fun(Arg) -> - {timeout, 60, {atom_to_list(Name), fun() -> - process_flag(trap_exit, true), - Fun(Arg) - end}} - end. - - -create_db() -> - DbName = ?tempdb(), - couch_db:create(DbName, [?ADMIN_CTX]). - - -populate_db(Db) -> - Docs = lists:map(fun(Idx) -> - DocId = lists:flatten(io_lib:format("~4..0b", [Idx])), - #doc{ - id = list_to_binary(DocId), - body = {[{<<"int">>, Idx}, {<<"vsn">>, 2}]} - } - end, lists:seq(1, 100)), - {ok, _} = couch_db:update_docs(Db, Docs). - - -open_doc(DbName, DocId) -> - couch_util:with_db(DbName, fun(Db) -> - couch_db:open_doc(Db, DocId, []) - end). - - -create_update(Doc, NewVsn) -> - #doc{ - id = DocId, - revs = {Pos, [Rev | _] = Revs}, - body = {Props} - } = Doc, - NewProps = lists:keyreplace(<<"vsn">>, 1, Props, {<<"vsn">>, NewVsn}), - NewRev = crypto:hash(md5, term_to_binary({DocId, Rev, {NewProps}})), - Doc#doc{ - revs = {Pos + 1, [NewRev | Revs]}, - body = {NewProps} - }. - - -purge_doc(DbName, DocId) -> - {ok, Doc} = open_doc(DbName, DocId), - {Pos, [Rev | _]} = Doc#doc.revs, - PInfo = {couch_uuids:random(), DocId, [{Pos, Rev}]}, - Resp = couch_util:with_db(DbName, fun(Db) -> - couch_db:purge_docs(Db, [PInfo], []) - end), - ?assertEqual({ok, [{ok, [{Pos, Rev}]}]}, Resp). - - -create_purge_checkpoint(DbName, PurgeSeq) -> - create_purge_checkpoint(DbName, PurgeSeq, tgt_node_bin()). - - -create_purge_checkpoint(DbName, PurgeSeq, TgtNode) when is_binary(TgtNode) -> - Resp = couch_util:with_db(DbName, fun(Db) -> - SrcUUID = couch_db:get_uuid(Db), - TgtUUID = couch_uuids:random(), - CPDoc = #doc{ - id = mem3_rep:make_purge_id(SrcUUID, TgtUUID), - body = {[ - {<<"target_node">>, TgtNode}, - {<<"purge_seq">>, PurgeSeq} - ]} - }, - couch_db:update_docs(Db, [CPDoc], []) - end), - ?assertMatch({ok, [_]}, Resp). - - -rpc_update_doc(DbName, Doc) -> - {Pos, [Rev | _]} = Doc#doc.revs, - RROpt = {read_repair, [{tgt_node(), [{Pos, Rev}]}]}, - rpc_update_doc(DbName, Doc, [RROpt]). - - -rpc_update_doc(DbName, Doc, Opts) -> - Ref = erlang:make_ref(), - put(rexi_from, {self(), Ref}), - fabric_rpc:update_docs(DbName, [Doc], Opts), - Reply = test_util:wait(fun() -> - receive - {Ref, Reply} -> - Reply - after 0 -> - wait - end - end), - ?assertEqual({ok, []}, Reply). - - -tgt_node() -> - 'foo@127.0.0.1'. - - -tgt_node_bin() -> - iolist_to_binary(atom_to_list(tgt_node())). |