#![cfg(test)]
use std::collections::{BTreeMap, BTreeSet};
use crate::persistent_artrie::PersistentARTrie;
use crate::Dictionary;
fn scratch(prefix: &str) -> tempfile::TempDir {
std::fs::create_dir_all("target/test-tmp").ok();
tempfile::Builder::new()
.prefix(prefix)
.tempdir_in("target/test-tmp")
.expect("scratch tempdir under target/test-tmp")
}
#[test]
fn m3_empty_term_routes_to_overlay() {
let dir = scratch("byte-m3-empty-term");
let path = dir.path().join("e.part");
let empty_count: u64 = 77;
let mut trie = PersistentARTrie::<u64>::create(&path).expect("create");
assert!(trie.route_overlay());
assert!(
trie.upsert_bytes(b"", empty_count)
.expect("overlay empty upsert"),
"upsert(\"\") newly inserted"
);
assert_eq!(
trie.get_value_bytes(b""),
Some(empty_count),
"empty-term get_value routes to the overlay root (empty-string support H5)"
);
assert!(
trie.contains_bytes(b""),
"contains(\"\") reads the overlay root final flag"
);
assert!(
!trie
.upsert_bytes(b"", 99)
.expect("overlay empty upsert overwrite"),
"second upsert(\"\") updates"
);
assert_eq!(trie.get_value_bytes(b""), Some(99));
assert_eq!(
trie.get_value_bytes(b"nonexistent"),
None,
"non-empty absent term routes to the overlay → None"
);
}
#[test]
fn m3_routed_writes_round_trip_and_survive_reopen() {
let dir = scratch("byte-m3-write-roundtrip");
let path = dir.path().join("w.part");
{
let mut trie = PersistentARTrie::<u64>::create(&path).expect("create");
assert!(trie.route_overlay());
assert!(
trie.insert_with_value("alpha", 10),
"insert_with_value newly inserted"
);
assert!(
!trie.insert_with_value("alpha", 999),
"insert_with_value is INSERT (no-op on existing)"
);
assert_eq!(trie.upsert_bytes(b"alpha", 11).expect("upsert"), false); assert!(trie.upsert_bytes(b"beta", 20).expect("upsert new")); assert_eq!(trie.increment_bytes(b"beta", 5).expect("increment"), 25);
assert_eq!(
trie.get_or_insert_bytes(b"gamma", 7)
.expect("get_or_insert"),
7
);
assert_eq!(
trie.get_or_insert_bytes(b"gamma", 100)
.expect("get_or_insert existing"),
7
);
let n = trie.insert_batch(&[
("delta".to_string(), Some(4)),
("epsilon".to_string(), Some(5)),
]);
assert_eq!(n, 2, "insert_batch newly inserted 2");
assert_eq!(trie.get_value_bytes(b"alpha"), Some(11));
assert_eq!(trie.get_value_bytes(b"beta"), Some(25));
assert_eq!(trie.get_value_bytes(b"gamma"), Some(7));
assert_eq!(trie.get_value_bytes(b"delta"), Some(4));
assert_eq!(trie.get_value_bytes(b"epsilon"), Some(5));
assert!(trie.remove("delta"));
assert_eq!(trie.get_value_bytes(b"delta"), None);
let removed = trie.remove_prefix(b"e");
assert_eq!(removed, 1, "remove_prefix removed epsilon");
assert_eq!(trie.get_value_bytes(b"epsilon"), None);
trie.sync().expect("sync");
}
let reopened = PersistentARTrie::<u64>::open(&path).expect("reopen");
assert_eq!(
reopened.get_value_bytes(b"alpha"),
Some(11),
"alpha durable through reopen"
);
assert_eq!(
reopened.get_value_bytes(b"beta"),
Some(25),
"beta durable through reopen"
);
assert_eq!(reopened.get_value_bytes(b"gamma"), Some(7));
assert_eq!(reopened.get_value_bytes(b"delta"), None, "delta removed");
assert_eq!(
reopened.get_value_bytes(b"epsilon"),
None,
"epsilon removed"
);
}
#[test]
fn m3_overlay_routed_ops_succeed() {
let dir = scratch("byte-m3-rejects");
let path = dir.path().join("r.part");
let other_path = dir.path().join("other.part");
let mut trie = PersistentARTrie::<u64>::create(&path).expect("create");
assert!(trie.route_overlay());
trie.increment_bytes(b"seed", 1).expect("seed");
assert!(
trie.compare_and_swap("seed", Some(1), 2)
.expect("CAS should succeed under overlay (NH2)"),
"compare_and_swap with matching expected now swaps under overlay"
);
assert_eq!(
trie.get_value_bytes(b"seed"),
Some(2),
"CAS swapped seed 1→2"
);
assert!(
!trie
.compare_and_swap_bytes(b"seed", Some(1), 9)
.expect("CAS mismatch returns Ok(false)"),
"compare_and_swap_bytes with a now-stale expected (1, current is 2) ⇒ no swap"
);
assert_eq!(
trie.get_value_bytes(b"seed"),
Some(2),
"failed CAS left seed unchanged"
);
let mut other = PersistentARTrie::<u64>::create(&other_path).expect("create other");
other.increment_bytes(b"x", 100).expect("other seed");
assert_eq!(
trie.merge_from(&other, |a, _| *a)
.expect("merge_from succeeds under overlay (C2)"),
1,
"merge_from now processes the one other-only term under the overlay"
);
assert_eq!(
trie.get_value_bytes(b"x"),
Some(100),
"merge_from inserted x"
);
assert_eq!(
trie.merge_replace(&other)
.expect("merge_replace succeeds under overlay (C2)"),
1,
"merge_replace now processes the overlapping term under the overlay"
);
assert_eq!(
trie.merge_from_batched(&other, |a, _| *a, 100)
.expect("merge_from_batched succeeds under overlay (C2)"),
1
);
assert_eq!(
trie.merge_from_batched_grouped(&other, |a, _| *a, 100)
.expect("merge_from_batched_grouped succeeds under overlay (C2)"),
1
);
let tx = trie
.begin_document("doc")
.expect("begin_document succeeds under overlay (C2)");
assert_eq!(
trie.commit_document(tx).expect("empty commit"),
0,
"an empty doc-tx commits 0 ops under the overlay"
);
let cfg = crate::persistent_artrie::CompactionConfig::default();
trie.compact(cfg, |_| {})
.expect("F6: compact succeeds under the overlay");
assert!(
trie.route_overlay(),
"F6: compact preserves the overlay regime"
);
}
#[test]
fn m3_deep_key_routed_reads_no_stack_overflow() {
let dir = scratch("byte-m3-deep");
let path = dir.path().join("deep.part");
let deep: Vec<u8> = vec![b'a'; 500];
let mut trie = PersistentARTrie::<u64>::create(&path).expect("create");
assert!(trie.route_overlay());
trie.increment_bytes(&deep, 11).expect("deep increment");
assert_eq!(Dictionary::len(&trie), Some(1));
assert_eq!(trie.get_value_bytes(&deep), Some(11));
let all: BTreeSet<Vec<u8>> = trie.iter().collect();
assert!(all.contains(&deep));
let with_values: BTreeMap<Vec<u8>, Option<u64>> = trie.iter_with_values().collect();
assert_eq!(with_values.get(&deep), Some(&Some(11)));
}