#!/usr/bin/env escript %% -*- erlang -*- % 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. -record(user_ctx, { name = null, roles = [], handler }). test_db_name() -> <<"couch_test_invalid_view_seq">>. main(_) -> test_util:init_code_path(), etap:plan(10), case (catch test()) of ok -> etap:end_tests(); Other -> etap:diag(io_lib:format("Test died abnormally: ~p", [Other])), etap:bail(Other) end, ok. %% NOTE: since during the test we stop the server, %% a huge and ugly but harmless stack trace is sent to stderr %% test() -> couch_server_sup:start_link(test_util:config_files()), timer:sleep(1000), delete_db(), create_db(), create_docs(), create_design_doc(), % make DB file backup backup_db_file(), put(addr, couch_config:get("httpd", "bind_address", "127.0.0.1")), put(port, integer_to_list(mochiweb_socket_server:get(couch_httpd, port))), create_new_doc(), query_view_before_restore_backup(), % restore DB file backup after querying view restore_backup_db_file(), query_view_after_restore_backup(), delete_db(), couch_server_sup:stop(), ok. admin_user_ctx() -> {user_ctx, #user_ctx{roles=[<<"_admin">>]}}. create_db() -> {ok, _} = couch_db:create(test_db_name(), [admin_user_ctx()]). delete_db() -> couch_server:delete(test_db_name(), [admin_user_ctx()]). create_docs() -> {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]), Doc1 = couch_doc:from_json_obj({[ {<<"_id">>, <<"doc1">>}, {<<"value">>, 1} ]}), Doc2 = couch_doc:from_json_obj({[ {<<"_id">>, <<"doc2">>}, {<<"value">>, 2} ]}), Doc3 = couch_doc:from_json_obj({[ {<<"_id">>, <<"doc3">>}, {<<"value">>, 3} ]}), {ok, _} = couch_db:update_docs(Db, [Doc1, Doc2, Doc3]), couch_db:ensure_full_commit(Db), couch_db:close(Db). create_design_doc() -> {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]), DDoc = couch_doc:from_json_obj({[ {<<"_id">>, <<"_design/foo">>}, {<<"language">>, <<"javascript">>}, {<<"views">>, {[ {<<"bar">>, {[ {<<"map">>, <<"function(doc) { emit(doc.value, 1); }">>} ]}} ]}} ]}), {ok, _} = couch_db:update_docs(Db, [DDoc]), couch_db:ensure_full_commit(Db), couch_db:close(Db). backup_db_file() -> DbFile = test_util:build_file("tmp/lib/" ++ binary_to_list(test_db_name()) ++ ".couch"), {ok, _} = file:copy(DbFile, DbFile ++ ".backup"), ok. create_new_doc() -> {ok, Db} = couch_db:open(test_db_name(), [admin_user_ctx()]), Doc666 = couch_doc:from_json_obj({[ {<<"_id">>, <<"doc666">>}, {<<"value">>, 999} ]}), {ok, _} = couch_db:update_docs(Db, [Doc666]), couch_db:ensure_full_commit(Db), couch_db:close(Db). db_url() -> "http://" ++ get(addr) ++ ":" ++ get(port) ++ "/" ++ binary_to_list(test_db_name()). query_view_before_restore_backup() -> {ok, Code, _Headers, Body} = test_util:request( db_url() ++ "/_design/foo/_view/bar", [], get), etap:is(Code, 200, "Got view response before restoring backup."), ViewJson = couch_util:json_decode(Body), Rows = couch_util:get_nested_json_value(ViewJson, [<<"rows">>]), HasDoc1 = has_doc("doc1", Rows), HasDoc2 = has_doc("doc2", Rows), HasDoc3 = has_doc("doc3", Rows), HasDoc666 = has_doc("doc666", Rows), etap:is(HasDoc1, true, "Before backup restore, view has doc1"), etap:is(HasDoc2, true, "Before backup restore, view has doc2"), etap:is(HasDoc3, true, "Before backup restore, view has doc3"), etap:is(HasDoc666, true, "Before backup restore, view has doc666"), ok. has_doc(DocId1, Rows) -> DocId = iolist_to_binary(DocId1), lists:any( fun({R}) -> lists:member({<<"id">>, DocId}, R) end, Rows ). restore_backup_db_file() -> couch_server_sup:stop(), timer:sleep(3000), DbFile = test_util:build_file("tmp/lib/" ++ binary_to_list(test_db_name()) ++ ".couch"), ok = file:delete(DbFile), ok = file:rename(DbFile ++ ".backup", DbFile), couch_server_sup:start_link(test_util:config_files()), timer:sleep(1000), put(port, integer_to_list(mochiweb_socket_server:get(couch_httpd, port))), ok. query_view_after_restore_backup() -> {ok, Code, _Headers, Body} = test_util:request( db_url() ++ "/_design/foo/_view/bar", [], get), etap:is(Code, 200, "Got view response after restoring backup."), ViewJson = couch_util:json_decode(Body), Rows = couch_util:get_nested_json_value(ViewJson, [<<"rows">>]), HasDoc1 = has_doc("doc1", Rows), HasDoc2 = has_doc("doc2", Rows), HasDoc3 = has_doc("doc3", Rows), HasDoc666 = has_doc("doc666", Rows), etap:is(HasDoc1, true, "After backup restore, view has doc1"), etap:is(HasDoc2, true, "After backup restore, view has doc2"), etap:is(HasDoc3, true, "After backup restore, view has doc3"), etap:is(HasDoc666, false, "After backup restore, view does not have doc666"), ok.