1% Licensed under the Apache License, Version 2.0 (the "License"); you may not 2% use this file except in compliance with the License. You may obtain a copy of 3% the License at 4% 5% http://www.apache.org/licenses/LICENSE-2.0 6% 7% Unless required by applicable law or agreed to in writing, software 8% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10% License for the specific language governing permissions and limitations under 11% the License. 12 13-module(cpse_test_purge_replication). 14-compile(export_all). 15-compile(nowarn_export_all). 16 17 18-include_lib("eunit/include/eunit.hrl"). 19-include_lib("couch/include/couch_db.hrl"). 20-include_lib("mem3/include/mem3.hrl"). 21 22 23setup_all() -> 24 cpse_util:setup_all([mem3, fabric, couch_replicator]). 25 26 27setup_each() -> 28 {ok, Src} = cpse_util:create_db(), 29 {ok, Tgt} = cpse_util:create_db(), 30 {couch_db:name(Src), couch_db:name(Tgt)}. 31 32 33teardown_each({SrcDb, TgtDb}) -> 34 ok = couch_server:delete(SrcDb, []), 35 ok = couch_server:delete(TgtDb, []). 36 37 38cpse_purge_http_replication({Source, Target}) -> 39 {ok, Rev1} = cpse_util:save_doc(Source, {[{'_id', foo}, {vsn, 1}]}), 40 41 cpse_util:assert_db_props(?MODULE, ?LINE, Source, [ 42 {doc_count, 1}, 43 {del_doc_count, 0}, 44 {update_seq, 1}, 45 {changes, 1}, 46 {purge_seq, 0}, 47 {purge_infos, []} 48 ]), 49 50 RepObject = {[ 51 {<<"source">>, db_url(Source)}, 52 {<<"target">>, db_url(Target)} 53 ]}, 54 55 {ok, _} = couch_replicator:replicate(RepObject, ?ADMIN_USER), 56 {ok, Doc1} = cpse_util:open_doc(Target, foo), 57 58 cpse_util:assert_db_props(?MODULE, ?LINE, Target, [ 59 {doc_count, 1}, 60 {del_doc_count, 0}, 61 {update_seq, 1}, 62 {changes, 1}, 63 {purge_seq, 0}, 64 {purge_infos, []} 65 ]), 66 67 PurgeInfos = [ 68 {cpse_util:uuid(), <<"foo">>, [Rev1]} 69 ], 70 71 {ok, [{ok, PRevs}]} = cpse_util:purge(Source, PurgeInfos), 72 ?assertEqual([Rev1], PRevs), 73 74 cpse_util:assert_db_props(?MODULE, ?LINE, Source, [ 75 {doc_count, 0}, 76 {del_doc_count, 0}, 77 {update_seq, 2}, 78 {changes, 0}, 79 {purge_seq, 1}, 80 {purge_infos, PurgeInfos} 81 ]), 82 83 % Show that a purge on the source is 84 % not replicated to the target 85 {ok, _} = couch_replicator:replicate(RepObject, ?ADMIN_USER), 86 {ok, Doc2} = cpse_util:open_doc(Target, foo), 87 [Rev2] = Doc2#doc_info.revs, 88 ?assertEqual(Rev1, Rev2#rev_info.rev), 89 ?assertEqual(Doc1, Doc2), 90 91 cpse_util:assert_db_props(?MODULE, ?LINE, Target, [ 92 {doc_count, 1}, 93 {del_doc_count, 0}, 94 {update_seq, 1}, 95 {changes, 1}, 96 {purge_seq, 0}, 97 {purge_infos, []} 98 ]), 99 100 % Show that replicating from the target 101 % back to the source reintroduces the doc 102 RepObject2 = {[ 103 {<<"source">>, db_url(Target)}, 104 {<<"target">>, db_url(Source)} 105 ]}, 106 107 {ok, _} = couch_replicator:replicate(RepObject2, ?ADMIN_USER), 108 {ok, Doc3} = cpse_util:open_doc(Source, foo), 109 [Revs3] = Doc3#doc_info.revs, 110 ?assertEqual(Rev1, Revs3#rev_info.rev), 111 112 cpse_util:assert_db_props(?MODULE, ?LINE, Source, [ 113 {doc_count, 1}, 114 {del_doc_count, 0}, 115 {update_seq, 3}, 116 {changes, 1}, 117 {purge_seq, 1}, 118 {purge_infos, PurgeInfos} 119 ]). 120 121 122cpse_purge_internal_repl_disabled({Source, Target}) -> 123 cpse_util:with_config([{"mem3", "replicate_purges", "false"}], fun() -> 124 repl(Source, Target), 125 126 {ok, [Rev1, Rev2]} = cpse_util:save_docs(Source, [ 127 {[{'_id', foo1}, {vsn, 1}]}, 128 {[{'_id', foo2}, {vsn, 2}]} 129 ]), 130 131 repl(Source, Target), 132 133 PurgeInfos1 = [ 134 {cpse_util:uuid(), <<"foo1">>, [Rev1]} 135 ], 136 {ok, [{ok, PRevs1}]} = cpse_util:purge(Source, PurgeInfos1), 137 ?assertEqual([Rev1], PRevs1), 138 139 PurgeInfos2 = [ 140 {cpse_util:uuid(), <<"foo2">>, [Rev2]} 141 ], 142 {ok, [{ok, PRevs2}]} = cpse_util:purge(Target, PurgeInfos2), 143 ?assertEqual([Rev2], PRevs2), 144 145 SrcShard = make_shard(Source), 146 TgtShard = make_shard(Target), 147 ?assertEqual({ok, 0}, mem3_rep:go(SrcShard, TgtShard)), 148 ?assertEqual({ok, 0}, mem3_rep:go(TgtShard, SrcShard)), 149 150 ?assertMatch({ok, #doc_info{}}, cpse_util:open_doc(Source, <<"foo2">>)), 151 ?assertMatch({ok, #doc_info{}}, cpse_util:open_doc(Target, <<"foo1">>)) 152 end). 153 154 155cpse_purge_repl_simple_pull({Source, Target}) -> 156 repl(Source, Target), 157 158 {ok, Rev} = cpse_util:save_doc(Source, {[{'_id', foo}, {vsn, 1}]}), 159 repl(Source, Target), 160 161 PurgeInfos = [ 162 {cpse_util:uuid(), <<"foo">>, [Rev]} 163 ], 164 {ok, [{ok, PRevs}]} = cpse_util:purge(Target, PurgeInfos), 165 ?assertEqual([Rev], PRevs), 166 repl(Source, Target). 167 168 169cpse_purge_repl_simple_push({Source, Target}) -> 170 repl(Source, Target), 171 172 {ok, Rev} = cpse_util:save_doc(Source, {[{'_id', foo}, {vsn, 1}]}), 173 repl(Source, Target), 174 175 PurgeInfos = [ 176 {cpse_util:uuid(), <<"foo">>, [Rev]} 177 ], 178 {ok, [{ok, PRevs}]} = cpse_util:purge(Source, PurgeInfos), 179 ?assertEqual([Rev], PRevs), 180 repl(Source, Target). 181 182 183repl(Source, Target) -> 184 SrcShard = make_shard(Source), 185 TgtShard = make_shard(Target), 186 187 ?assertEqual({ok, 0}, mem3_rep:go(SrcShard, TgtShard)), 188 189 SrcTerm = cpse_util:db_as_term(Source, replication), 190 TgtTerm = cpse_util:db_as_term(Target, replication), 191 192 Diff = cpse_util:term_diff(SrcTerm, TgtTerm), 193 ?assertEqual(nodiff, Diff). 194 195 196make_shard(DbName) -> 197 #shard{ 198 name = DbName, 199 node = node(), 200 dbname = DbName, 201 range = [0, 16#FFFFFFFF] 202 }. 203 204 205db_url(DbName) -> 206 Addr = config:get("httpd", "bind_address", "127.0.0.1"), 207 Port = mochiweb_socket_server:get(couch_httpd, port), 208 Url = ?l2b(io_lib:format("http://~s:~b/~s", [Addr, Port, DbName])), 209 test_util:wait(fun() -> 210 case test_request:get(?b2l(Url)) of 211 {ok, 200, _, _} -> ok; 212 _ -> wait 213 end 214 end), 215 Url. 216