Merge remote branch 'github/pr/160'
This closes #160
Signed-off-by: ILYA Khlopotov <iilyak@ca.ibm.com>
diff --git a/src/couch_db.erl b/src/couch_db.erl
index 1a9f669..8260a5c 100644
--- a/src/couch_db.erl
+++ b/src/couch_db.erl
@@ -35,6 +35,7 @@
-export([monitored_by/1]).
-export([normalize_dbname/1]).
-export([validate_dbname/1]).
+-export([dbname_suffix/1]).
-include_lib("couch/include/couch_db.hrl").
@@ -1513,10 +1514,23 @@
select_lt(V1, V2) when V1 > V2 -> V2;
select_lt(V1, _V2) -> V1.
-normalize_dbname(<<"shards/", _/binary>> = Path) ->
- lists:last(binary:split(mem3:dbname(Path), <<"/">>, [global]));
-normalize_dbname(DbName) ->
- DbName.
+-spec normalize_dbname(list() | binary()) -> binary().
+
+normalize_dbname(DbName) when is_list(DbName) ->
+ normalize_dbname(list_to_binary(DbName));
+normalize_dbname(DbName) when is_binary(DbName) ->
+ case filename:extension(DbName) of
+ <<".couch">> ->
+ mem3:dbname(filename:rootname(DbName));
+ _ ->
+ mem3:dbname(DbName)
+ end.
+
+-spec dbname_suffix(list() | binary()) -> binary().
+
+dbname_suffix(DbName) ->
+ filename:basename(normalize_dbname(DbName)).
+
validate_dbname(DbName) when is_list(DbName) ->
validate_dbname(?l2b(DbName));
@@ -1542,7 +1556,103 @@
is_systemdb(DbName) when is_list(DbName) ->
is_systemdb(?l2b(DbName));
-is_systemdb(<<"shards/", _/binary>> = Path) when is_binary(Path) ->
- is_systemdb(normalize_dbname(Path));
is_systemdb(DbName) when is_binary(DbName) ->
- lists:member(DbName, ?SYSTEM_DATABASES).
+ lists:member(dbname_suffix(DbName), ?SYSTEM_DATABASES).
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+setup() ->
+ ok = meck:new(couch_db_plugin, [passthrough]),
+ ok = meck:expect(couch_db_plugin, validate_dbname, fun(_, _) -> false end),
+ ok.
+
+teardown(_) ->
+ (catch meck:unload(couch_db_plugin)).
+
+validate_dbname_success_test_() ->
+ Cases =
+ generate_cases_with_shards("long/co$mplex-/path+/_something")
+ ++ generate_cases_with_shards("something")
+ ++ lists:append(
+ [generate_cases_with_shards(?b2l(SystemDb))
+ || SystemDb <- ?SYSTEM_DATABASES]),
+ {
+ foreach, fun setup/0, fun teardown/1,
+ [{test_name(A), fun() -> should_pass_validate_dbname(A) end} || {_, A} <- Cases]
+ }.
+
+validate_dbname_fail_test_() ->
+ Cases = generate_cases("_long/co$mplex-/path+/_something")
+ ++ generate_cases("_something")
+ ++ generate_cases_with_shards("long/co$mplex-/path+/_something#")
+ ++ generate_cases_with_shards("long/co$mplex-/path+/some.thing"),
+ {
+ foreach, fun setup/0, fun teardown/1,
+ [{test_name(A), fun() -> should_fail_validate_dbname(A) end} || {_, A} <- Cases]
+ }.
+
+normalize_dbname_test_() ->
+ Cases = generate_cases_with_shards("long/co$mplex-/path+/_something")
+ ++ generate_cases_with_shards("_something"),
+ WithExpected = [{?l2b(filename:rootname(A)), B} || {A, B} <- Cases],
+ [{test_name({Expected, Db}), ?_assertEqual(Expected, normalize_dbname(Db))}
+ || {Expected, Db} <- WithExpected].
+
+dbname_suffix_test_() ->
+ Cases = generate_cases_with_shards("long/co$mplex-/path+/_something")
+ ++ generate_cases_with_shards("_something"),
+ WithExpected = [{?l2b(filename:basename(Arg)), Db} || {Arg, Db} <- Cases],
+ [{test_name({Expected, Db}), ?_assertEqual(Expected, dbname_suffix(Db))}
+ || {Expected, Db} <- WithExpected].
+
+is_systemdb_test_() ->
+ Cases = lists:append([
+ generate_cases_with_shards("long/co$mplex-/path+/" ++ ?b2l(Db))
+ || Db <- ?SYSTEM_DATABASES]
+ ++ [generate_cases_with_shards(?b2l(Db)) || Db <- ?SYSTEM_DATABASES
+ ]),
+ WithExpected = [{?l2b(filename:basename(filename:rootname(Arg))), Db}
+ || {Arg, Db} <- Cases],
+ [{test_name({Expected, Db}) ++ " in ?SYSTEM_DATABASES",
+ ?_assert(is_systemdb(Db))} || {Expected, Db} <- WithExpected].
+
+should_pass_validate_dbname(DbName) ->
+ {test_name(DbName), ?_assertEqual(ok, validate_dbname(DbName))}.
+
+should_fail_validate_dbname(DbName) ->
+ {test_name(DbName), ?_test(begin
+ Result = validate_dbname(DbName),
+ ?assertMatch({error, {illegal_database_name, _}}, Result),
+ {error, {illegal_database_name, FailedDbName}} = Result,
+ ?assertEqual(to_binary(DbName), FailedDbName),
+ ok
+ end)}.
+
+to_binary(DbName) when is_list(DbName) ->
+ ?l2b(DbName);
+to_binary(DbName) when is_binary(DbName) ->
+ DbName.
+
+test_name({Expected, DbName}) ->
+ lists:flatten(io_lib:format("~p -> ~p", [DbName, Expected]));
+test_name(DbName) ->
+ lists:flatten(io_lib:format("~p", [DbName])).
+
+generate_cases_with_shards(DbName) ->
+ DbNameWithShard = add_shard(DbName),
+ DbNameWithShardAndExtension = add_shard(DbName) ++ ".couch",
+ Cases = [
+ DbName, ?l2b(DbName),
+ DbNameWithShard, ?l2b(DbNameWithShard),
+ DbNameWithShardAndExtension, ?l2b(DbNameWithShardAndExtension)
+ ],
+ [{DbName, Case} || Case <- Cases].
+
+add_shard(DbName) ->
+ "shards/00000000-3fffffff/" ++ DbName ++ ".1415960794".
+
+generate_cases(DbName) ->
+ [{DbName, DbName}, {DbName, ?l2b(DbName)}].
+
+-endif.