blob: d99062b2bc4996c9d9b58469e89f5ae689e2e24c [file] [log] [blame]
% 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_replicator_filtered_tests).
-include_lib("couch/include/couch_eunit.hrl").
-include_lib("couch/include/couch_db.hrl").
-define(DDOC,
{[
{<<"_id">>, <<"_design/filter_ddoc">>},
{<<"filters">>,
{[
{<<"testfilter">>, <<
"\n"
" function(doc, req){if (doc.class == 'mammal') return true;}\n"
" "
>>},
{<<"queryfilter">>, <<
"\n"
" function(doc, req) {\n"
" if (doc.class && req.query.starts) {\n"
" return doc.class.indexOf(req.query.starts) === 0;\n"
" }\n"
" else {\n"
" return false;\n"
" }\n"
" }\n"
" "
>>}
]}},
{<<"views">>,
{[
{<<"mammals">>,
{[
{<<"map">>, <<
"\n"
" function(doc) {\n"
" if (doc.class == 'mammal') {\n"
" emit(doc._id, null);\n"
" }\n"
" }\n"
" "
>>}
]}}
]}}
]}
).
filtered_replication_test_() ->
{
"Filtered replication tests",
{
foreach,
fun couch_replicator_test_helper:test_setup/0,
fun couch_replicator_test_helper:test_teardown/1,
[
?TDEF_FE(should_succeed),
?TDEF_FE(should_succeed_with_query),
?TDEF_FE(should_succeed_with_view)
]
}
}.
should_succeed({_Ctx, {Source, Target}}) ->
create_docs(Source),
RepObject =
{[
{<<"source">>, db_url(Source)},
{<<"target">>, db_url(Target)},
{<<"filter">>, <<"filter_ddoc/testfilter">>}
]},
replicate(RepObject),
%% FilteredFun is an Erlang version of following JS function
%% function(doc, req){if (doc.class == 'mammal') return true;}
FilterFun = fun(_DocId, #doc{body = {Props}}) ->
couch_util:get_value(<<"class">>, Props) == <<"mammal">>
end,
{TargetDocCount, AllReplies} = compare_dbs(Source, Target, FilterFun),
% Target DB has proper number of docs,
?assertEqual(1, TargetDocCount),
% All the docs filtered as expected
?assert(lists:all(fun(Valid) -> Valid end, AllReplies)).
should_succeed_with_query({_Ctx, {Source, Target}}) ->
create_docs(Source),
RepObject =
{[
{<<"source">>, db_url(Source)},
{<<"target">>, db_url(Target)},
{<<"filter">>, <<"filter_ddoc/queryfilter">>},
{<<"query_params">>,
{[
{<<"starts">>, <<"a">>}
]}}
]},
replicate(RepObject),
FilterFun = fun(_DocId, #doc{body = {Props}}) ->
case couch_util:get_value(<<"class">>, Props) of
<<"a", _/binary>> -> true;
_ -> false
end
end,
{TargetDocCount, AllReplies} = compare_dbs(Source, Target, FilterFun),
% Target DB has proper number of docs
?assertEqual(2, TargetDocCount),
% All the docs filtered as expected,
?assert(lists:all(fun(Valid) -> Valid end, AllReplies)).
should_succeed_with_view({_Ctx, {Source, Target}}) ->
create_docs(Source),
RepObject =
{[
{<<"source">>, db_url(Source)},
{<<"target">>, db_url(Target)},
{<<"filter">>, <<"_view">>},
{<<"query_params">>,
{[
{<<"view">>, <<"filter_ddoc/mammals">>}
]}}
]},
replicate(RepObject),
FilterFun = fun(_DocId, #doc{body = {Props}}) ->
couch_util:get_value(<<"class">>, Props) == <<"mammal">>
end,
{TargetDocCount, AllReplies} = compare_dbs(Source, Target, FilterFun),
% Target DB has proper number of docs
?assertEqual(1, TargetDocCount),
% All the docs filtered as expected
?assert(lists:all(fun(Valid) -> Valid end, AllReplies)).
compare_dbs(Source, Target, FilterFun) ->
{ok, TargetDocCount} = fabric:get_doc_count(Target),
Replies = lists:foldl(
fun({Id, Rev}, Acc) ->
SrcDoc = read_doc(Source, Id, Rev),
TgtDoc = read_doc(Target, Id, Rev),
case FilterFun(Id, SrcDoc) of
true ->
[is_record(TgtDoc, doc) | Acc];
false ->
[TgtDoc =:= not_found | Acc]
end
end,
[],
couch_replicator_test_helper:cluster_doc_revs(Source)
),
{TargetDocCount, Replies}.
read_doc(Db, DocId, Rev) ->
couch_replicator_test_helper:cluster_open_rev(Db, DocId, Rev).
create_docs(DbName) ->
Docs = [
couch_doc:from_json_obj(?DDOC),
#doc{
id = <<"doc1">>,
body =
{[
{<<"class">>, <<"mammal">>},
{<<"value">>, 1}
]}
},
#doc{
id = <<"doc2">>,
body =
{[
{<<"class">>, <<"amphibians">>},
{<<"value">>, 2}
]}
},
#doc{
id = <<"doc3">>,
body =
{[
{<<"class">>, <<"reptiles">>},
{<<"value">>, 3}
]}
},
#doc{
id = <<"doc4">>,
body =
{[
{<<"class">>, <<"arthropods">>},
{<<"value">>, 2}
]}
}
],
{ok, [_ | _]} = fabric:update_docs(DbName, Docs, [?ADMIN_CTX]).
db_url(DbName) ->
couch_replicator_test_helper:cluster_db_url(DbName).
replicate(RepObject) ->
couch_replicator_test_helper:replicate(RepObject).