let basic_test (module Test_db : TEST_DATABASE) uri_string () =
let open Test_db in
let open Trakeva.Action in
let local_assert name c =
Test.check (name :: test_name :: uri_string :: []) c in
DB.load uri_string
>>= fun db ->
let test_get ?(handle=db) ?collection k f =
DB.get handle ?collection ~key:k
>>= fun opt ->
local_assert (sprintf "get %s/%s" (Option.value collection ~default:"") k)
(f opt);
return () in
let is_none = ((=) None) in
test_get "k" is_none >>= fun () ->
test_get ~collection:"c" "k" is_none >>= fun () ->
DB.get_all db ~collection:"c"
>>= fun list ->
local_assert "all c" (list = []);
let test_actions res actions =
let action = seq actions in
DB.act db ~action
>>= function
| r when r = res -> return ()
| `Done ->
ksprintf Test.fail "Action %s should be Not_done" (to_string action);
return ()
| `Not_done ->
ksprintf Test.fail "Action %s should be Done" (to_string action);
return ()
in
test_actions `Done [set ~key:"k" "v0"] >>= fun () ->
test_get "k" ((=) (Some "v0")) >>= fun () ->
test_actions `Done [set ~key:"k" "v"] >>= fun () ->
test_get "k" ((=) (Some "v")) >>= fun () ->
test_actions `Done [
contains ~key:"k" "v";
unset "k";
set ~key:"k1" ~collection:"c" "V";
] >>= fun () ->
test_actions `Done [
set ~key:"thekey" ~collection:"c1" "v1";
set ~key:"thekey" ~collection:"c2" "v2";
]
>>= fun () ->
test_get ?collection:None "thekey" ((=) None) >>= fun () ->
test_get ~collection:"c1" "thekey" ((=) (Some "v1")) >>= fun () ->
test_get ~collection:"c2" "thekey" ((=) (Some "v2")) >>= fun () ->
test_actions `Not_done [
contains ~key:"k" "v";
set ~key:"k1" ~collection:"c" "V";
] >>= fun () ->
test_actions `Done [
is_not_set "k";
set ~key:"k2" ~collection:"c" "V2";
set ~key:"k3" ~collection:"c" "V3";
set ~key:"k4" ~collection:"c" "V4";
set ~key:"k5" ~collection:"c" "V5";
] >>= fun () ->
DB.get_all db ~collection:"c"
>>= fun list ->
local_assert "full collection 'c'"
(List.sort ~cmp:String.compare list = ["k1"; "k2"; "k3"; "k4"; "k5"]);
let key = "K" in
let insane =
let buf = Bytes.make 256 '\000' in
for i = 0 to 255 do Bytes.set buf i (char_of_int i) done;
Bytes.to_string buf in
test_actions `Done [
set ~key "\"";
set ~key "\\\"";
set ~key "\"'\"";
set ~key:insane insane;
]
>>= fun () ->
test_get ?collection:None insane ((=) (Some insane))
>>= fun () ->
let to_list res_stream =
let rec go acc =
res_stream ()
>>= function
| None -> return acc
| Some v -> go (v :: acc) in
go []
in
let list_equal l1 l2 =
let prep = List.sort ~cmp:Pervasives.compare in
match prep l1 = prep l2 with
| true -> true
| false ->
say "[%s] ≠ [%s]"
(String.concat ~sep:", " l1)
(String.concat ~sep:", " l2);
false
in
let check_iterator_like_get_all ~collection =
let stream = DB.iterator db ~collection in
to_list stream
>>= fun all ->
DB.get_all db ~collection
>>= fun all_too ->
local_assert (sprintf "iter %s" collection) (list_equal all all_too);
return ()
in
check_iterator_like_get_all "c" >>= fun () ->
check_iterator_like_get_all "cc" >>= fun () ->
let make_self_collection ~collection =
let keyvalues = List.init 10 ~f:(sprintf "kv%d") in
let actions = List.map keyvalues ~f:(fun kv -> set ~collection ~key:kv kv) in
test_actions `Done actions
>>= fun () ->
return keyvalues
in
let iterate_and_set ~collection =
let stream = DB.iterator db ~collection in
let rec go_through () =
stream () >>= function
| Some kv ->
test_actions `Done [set ~collection ~key:kv ("SET" ^ kv)]
>>= fun () ->
go_through ()
| None -> return ()
in
go_through ()
in
let test_rw_interleave collection =
make_self_collection ~collection
>>= fun keyvalues ->
iterate_and_set ~collection
>>= fun () ->
check_iterator_like_get_all collection
>>= fun () ->
DB.get_all db ~collection
>>= fun allnew ->
local_assert (sprintf "test_rw_interleave %s" collection)
(list_equal keyvalues allnew);
return ()
in
test_rw_interleave "ccc"
>>= fun () ->
let iterate_and_delete ~collection ~at ~all =
let stream = DB.iterator db ~collection in
let rec go_through remaining =
stream () >>= function
| Some kv when remaining = 0 ->
say "%s got some %S in the stream" Test_db.test_name kv;
test_actions `Done (List.map all ~f:(fun key -> unset ~collection key))
>>= fun () ->
go_through (-1)
| Some kv ->
say "%s got some %S in the stream : remaining: %d" Test_db.test_name kv remaining;
go_through (remaining - 1)
| None -> return ()
in
go_through at
in
let test_delete_iterleave collection =
make_self_collection ~collection
>>= fun keyvalues ->
iterate_and_delete
~collection ~all:keyvalues ~at:(List.length keyvalues / 2)
>>= fun () ->
DB.get_all db ~collection
>>= fun allnew ->
local_assert ("test_delete_iterleave") (allnew = []);
return ()
in
test_delete_iterleave "aaa"
>>= fun () ->
let bunch_of_ints = List.init 100 ~f:(fun i -> i) in
Deferred_list.for_concurrent bunch_of_ints ~f:(function
| n when n mod 2 = 0 ->
let v = sprintf "%d" n in
test_actions `Done [set ~key:v v]
| n ->
DB.get db ~key
>>= fun _ ->
return ()
)
>>= fun ((_ : unit list), errors) ->
begin match errors with
| [] -> return ()
| more ->
let msg =
sprintf "concurrent errors: %s"
(List.map more ~f:(function
| `Database (`Get _, m)
| `Database (`Act _, m) -> m)
|> String.concat ~sep:"; ") in
local_assert msg false;
return ()
end
>>= fun () ->
DB.close db