1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// SPDX-License-Identifier: Apache-2.0
//! ยง24 KV engine gate tests โ BETA narrow subset for NodeDB-Lite 0.1.0.
//!
//! Scope: put / get / delete only.
//! TTL and sorted-index are EXPERIMENTAL and are NOT exercised here.
//!
//! The KV implementation lives in `nodedb/collection/kv.rs` and is not a
//! standalone engine module. Both sync modes (direct-kv and CRDT-backed)
//! share the same public `kv_put` / `kv_get` / `kv_delete` surface tested
//! below.
use nodedb_lite::{NodeDbLite, PagedbStorageMem};
async fn open_db() -> std::sync::Arc<NodeDbLite<PagedbStorageMem>> {
let storage = PagedbStorageMem::open_in_memory()
.await
.expect("open in-memory storage");
NodeDbLite::open(storage).await.expect("open NodeDbLite")
}
// ---------------------------------------------------------------------------
// kv_put_get_delete_roundtrip
// ---------------------------------------------------------------------------
/// Put 5 keys, get each back asserting value matches, delete 2, re-get those
/// asserting absent, re-get remaining 3 asserting still present.
#[tokio::test]
async fn kv_put_get_delete_roundtrip() {
let db = open_db().await;
let col = "gate_roundtrip";
let pairs: &[(&str, &[u8])] = &[
("key1", b"value_one"),
("key2", b"value_two"),
("key3", b"value_three"),
("key4", b"value_four"),
("key5", b"value_five"),
];
// Insert all 5 keys.
for (k, v) in pairs {
db.kv_put(col, k, v).await.expect("kv_put");
}
// Flush to ensure persistence layer is exercised.
db.kv_flush().await.expect("kv_flush");
// Get each key back and assert value matches.
for (k, expected) in pairs {
let got = db.kv_get(col, k).await.expect("kv_get");
assert_eq!(
got.as_deref(),
Some(*expected),
"value mismatch for key {k}"
);
}
// Delete key2 and key4.
db.kv_delete(col, "key2").await.expect("kv_delete key2");
db.kv_delete(col, "key4").await.expect("kv_delete key4");
db.kv_flush().await.expect("kv_flush after deletes");
// Deleted keys must be absent.
let gone2 = db
.kv_get(col, "key2")
.await
.expect("kv_get key2 after delete");
assert!(gone2.is_none(), "key2 should be absent after delete");
let gone4 = db
.kv_get(col, "key4")
.await
.expect("kv_get key4 after delete");
assert!(gone4.is_none(), "key4 should be absent after delete");
// Remaining keys must still be present.
for k in ["key1", "key3", "key5"] {
let expected = pairs
.iter()
.find(|(pk, _)| *pk == k)
.map(|(_, v)| *v)
.expect("pair exists");
let got = db.kv_get(col, k).await.expect("kv_get remaining");
assert_eq!(
got.as_deref(),
Some(expected),
"value mismatch for surviving key {k}"
);
}
}
// ---------------------------------------------------------------------------
// kv_get_missing_returns_none_or_error
// ---------------------------------------------------------------------------
/// Get a never-inserted key and assert the API returns `None` (the overlay
/// path) and then `None` from the KV store as well (after a flush with no writes).
#[tokio::test]
async fn kv_get_missing_returns_none_or_error() {
let db = open_db().await;
let col = "gate_missing";
// Query a key that was never inserted.
let result = db
.kv_get(col, "never_inserted_key")
.await
.expect("kv_get should not error for missing key");
assert!(
result.is_none(),
"expected None for a missing key, got {result:?}"
);
// Also verify that a flush + re-get still returns None (not an error).
db.kv_flush().await.expect("kv_flush");
let result2 = db
.kv_get(col, "never_inserted_key")
.await
.expect("kv_get after flush should not error");
assert!(
result2.is_none(),
"expected None after flush for a missing key, got {result2:?}"
);
}