summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorPaul Joseph Davis <davisp@apache.org>2010-02-18 19:55:55 +0000
committerPaul Joseph Davis <davisp@apache.org>2010-02-18 19:55:55 +0000
commit0f3bc594c57d048d329708a6c91edf5bb5be3719 (patch)
tree8cf5a900df7fa783b8ce174dc5df57ead6bd750e /test
parent6c2f190d314c3894a9fddb4c1c3a65f5ddca3060 (diff)
Fixes COUCHDB-640 - Reset views with invalid update seqs.
If a database is restored to a previous version, and the view files are not updated appropriately they will contain data from the database after the reverted-to-update_seq. This patch checks that the update sequence recorded in a view is less than or equal to the update sequence of the database. Patch is by Filipe Manana. Execellent work once again. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@911544 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'test')
-rwxr-xr-xtest/etap/150-invalid-view-seq.t192
-rw-r--r--test/etap/Makefile.am6
2 files changed, 197 insertions, 1 deletions
diff --git a/test/etap/150-invalid-view-seq.t b/test/etap/150-invalid-view-seq.t
new file mode 100755
index 00000000..0664c116
--- /dev/null
+++ b/test/etap/150-invalid-view-seq.t
@@ -0,0 +1,192 @@
+#!/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
+}).
+
+default_config() ->
+ test_util:build_file("etc/couchdb/default_dev.ini").
+
+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([default_config()]),
+ 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, couch_config:get("httpd", "port", "5984")),
+ application:start(inets),
+
+ 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}} = http:request(
+ get,
+ {db_url() ++ "/_design/foo/_view/bar", []},
+ [],
+ [{sync, true}]),
+ 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([default_config()]),
+ timer:sleep(1000),
+ ok.
+
+query_view_after_restore_backup() ->
+ {ok, {{_, Code, _}, _Headers, Body}} = http:request(
+ get,
+ {db_url() ++ "/_design/foo/_view/bar", []},
+ [],
+ [{sync, true}]),
+ 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.
diff --git a/test/etap/Makefile.am b/test/etap/Makefile.am
index 042c0bc2..2e302194 100644
--- a/test/etap/Makefile.am
+++ b/test/etap/Makefile.am
@@ -61,4 +61,8 @@ EXTRA_DIST = \
120-stats-collect.t \
121-stats-aggregates.cfg \
121-stats-aggregates.ini \
- 121-stats-aggregates.t
+ 121-stats-aggregates.t \
+ 130-attachments-md5.t \
+ 140-attachment-comp.t \
+ 150-invalid-view-seq.t
+