| % 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(couch_index_ddoc_updated_tests). |
| |
| -include_lib("couch/include/couch_eunit.hrl"). |
| -include_lib("couch/include/couch_db.hrl"). |
| |
| |
| start() -> |
| fake_index(), |
| Ctx = test_util:start_couch([mem3, fabric]), |
| DbName = ?tempdb(), |
| ok = fabric:create_db(DbName, [?ADMIN_CTX]), |
| {Ctx, DbName}. |
| |
| |
| stop({Ctx, DbName}) -> |
| meck:unload(test_index), |
| ok = fabric:delete_db(DbName, [?ADMIN_CTX]), |
| DbDir = config:get("couchdb", "database_dir", "."), |
| WaitFun = fun() -> |
| filelib:fold_files(DbDir, <<".*", DbName/binary, "\.[0-9]+.*">>, |
| true, fun(_F, _A) -> wait end, ok) |
| end, |
| ok = test_util:wait(WaitFun), |
| test_util:stop_couch(Ctx), |
| ok. |
| |
| |
| ddoc_update_test_() -> |
| { |
| "Check ddoc update actions", |
| { |
| setup, |
| fun start/0, fun stop/1, |
| fun check_all_indexers_exit_on_ddoc_change/1 |
| } |
| }. |
| |
| |
| check_all_indexers_exit_on_ddoc_change({_Ctx, DbName}) -> |
| ?_test(begin |
| [DbShard1 | RestDbShards] = lists:map(fun(Sh) -> |
| {ok, ShardDb} = couch_db:open(mem3:name(Sh), []), |
| ShardDb |
| end, mem3:local_shards(mem3:dbname(DbName))), |
| |
| % create a DDoc on Db1 |
| DDocID = <<"idx_name">>, |
| DDocJson = couch_doc:from_json_obj({[ |
| {<<"_id">>, DDocID}, |
| {<<"value">>, 1} |
| ]}), |
| {ok, _Rev} = couch_db:update_doc(DbShard1, DDocJson, []), |
| {ok, DbShard} = couch_db:reopen(DbShard1), |
| {ok, DDoc} = couch_db:open_doc( |
| DbShard, DDocID, [ejson_body, ?ADMIN_CTX]), |
| DbShards = [DbShard | RestDbShards], |
| N = length(DbShards), |
| |
| % run couch_index process for each shard database |
| ok = meck:reset(test_index), |
| lists:foreach(fun(ShardDb) -> |
| couch_index_server:get_index(test_index, ShardDb, DDoc) |
| end, DbShards), |
| |
| IndexesBefore = get_indexes_by_ddoc(DDocID, N), |
| ?assertEqual(N, length(IndexesBefore)), |
| |
| AliveBefore = lists:filter(fun erlang:is_process_alive/1, IndexesBefore), |
| ?assertEqual(N, length(AliveBefore)), |
| |
| % update ddoc |
| DDocJson2 = couch_doc:from_json_obj({[ |
| {<<"_id">>, DDocID}, |
| {<<"value">>, 2}, |
| {<<"_rev">>, couch_doc:rev_to_str(DDoc#doc.revs)} |
| ]}), |
| {ok, _} = couch_db:update_doc(DbShard, DDocJson2, []), |
| |
| % assert that all index processes exit after ddoc updated |
| ok = meck:reset(test_index), |
| couch_index_server:handle_db_event( |
| couch_db:name(DbShard), {ddoc_updated, DDocID}, {st, ""}), |
| |
| ok = meck:wait(N, test_index, init, ['_', '_'], 5000), |
| IndexesAfter = get_indexes_by_ddoc(DDocID, 0), |
| ?assertEqual(0, length(IndexesAfter)), |
| |
| %% assert that previously running indexes are gone |
| AliveAfter = lists:filter(fun erlang:is_process_alive/1, IndexesBefore), |
| ?assertEqual(0, length(AliveAfter)), |
| ok |
| end). |
| |
| |
| fake_index() -> |
| ok = meck:new([test_index], [non_strict]), |
| ok = meck:expect(test_index, init, fun(Db, DDoc) -> |
| {ok, {couch_db:name(Db), DDoc}} |
| end), |
| ok = meck:expect(test_index, open, fun(_Db, State) -> |
| {ok, State} |
| end), |
| ok = meck:expect(test_index, get, fun |
| (db_name, {DbName, _DDoc}) -> |
| DbName; |
| (idx_name, {_DbName, DDoc}) -> |
| DDoc#doc.id; |
| (signature, {_DbName, DDoc}) -> |
| couch_hash:md5_hash(term_to_binary(DDoc)); |
| (update_seq, Seq) -> |
| Seq |
| end), |
| ok = meck:expect(test_index, shutdown, ['_'], ok). |
| |
| |
| get_indexes_by_ddoc(DDocID, N) -> |
| Indexes = test_util:wait(fun() -> |
| Indxs = ets:match_object( |
| couchdb_indexes_by_db, {'$1', {DDocID, '$2'}}), |
| case length(Indxs) == N of |
| true -> |
| Indxs; |
| false -> |
| wait |
| end |
| end), |
| lists:foldl(fun({DbName, {_DDocID, Sig}}, Acc) -> |
| case ets:lookup(couchdb_indexes_by_sig, {DbName, Sig}) of |
| [{_, Pid}] -> [Pid|Acc]; |
| _ -> Acc |
| end |
| end, [], Indexes). |
| |