use std::error::Error;
use crate::branch::conflict::ConflictPolicy;
use crate::branch::handle::DEFAULT_SHARD_ID;
use crate::branch::lifecycle::create_branch;
use crate::branch::merge::merge_with_report;
use crate::branch::refstore::BranchRefStore;
use crate::branch::registry::BranchRegistry;
use crate::branch::{CommitDurability, CommitRequest, commit_branch};
use crate::store::MemoryStore;
use crate::sync::ballot::{Ballot, Stamp};
use crate::sync::topology::SyncNodeId;
use crate::sync::{SyncMergeRoots, merge_committed_union, merge_synced_roots};
use crate::tree::{Hash, LeafNode, Node, TreePolicy, batch_mutate, insert};
use crate::ttl::entry::encode_stamped;
use crate::wal::WalBuffer;
type TestResult = Result<(), Box<dyn Error>>;
const fn v2_policy() -> TreePolicy {
TreePolicy::v2(48, 48)
}
fn oversized(seed: u8) -> Vec<u8> {
vec![seed; 40]
}
fn map() -> Vec<(Vec<u8>, Option<Vec<u8>>)> {
(0u8..4)
.map(|n| (format!("k{n:02}").into_bytes(), Some(oversized(n))))
.collect()
}
fn empty_root(store: &mut MemoryStore) -> Result<Hash, Box<dyn Error>> {
Ok(store.put(&Node::Leaf(LeafNode::new(Vec::new())?)))
}
fn canonical(store: &mut MemoryStore, policy: TreePolicy) -> Result<Hash, Box<dyn Error>> {
let root = empty_root(store)?;
Ok(batch_mutate(store, root, map().as_slice(), policy)?)
}
#[test]
fn fixture_v2_and_v1_roots_differ() -> TestResult {
let mut store = MemoryStore::new();
let v2_root = canonical(&mut store, v2_policy())?;
let v1_root = canonical(&mut store, TreePolicy::V1_DEFAULT)?;
assert_ne!(
v2_root, v1_root,
"v2 oversized-isolation must produce a different root than v1 packing"
);
Ok(())
}
#[test]
fn public_tree_api_inserts_are_v2_shaped() -> TestResult {
let mut store = MemoryStore::new();
let canonical_v2 = canonical(&mut store, v2_policy())?;
let mut root = empty_root(&mut store)?;
for (key, value) in map() {
root = insert(
&mut store,
root,
&key,
value.as_deref().unwrap_or_default(),
v2_policy(),
)?;
}
assert_eq!(root, canonical_v2, "insert-under-v2 must reach the v2 root");
assert_ne!(root, canonical(&mut store, TreePolicy::V1_DEFAULT)?);
Ok(())
}
#[test]
fn wal_buffer_commit_dispatches_v2() -> TestResult {
let mut store = MemoryStore::new();
let canonical_v2 = canonical(&mut store, v2_policy())?;
let canonical_v1 = canonical(&mut store, TreePolicy::V1_DEFAULT)?;
let base = empty_root(&mut store)?;
let mut buffer = WalBuffer::new();
for (key, value) in map() {
buffer.put(&key, value.unwrap_or_default());
}
let v2_root = buffer.commit(base, &mut store, v2_policy())?;
assert_eq!(v2_root, canonical_v2, "WalBuffer::commit must build v2");
assert_ne!(v2_root, canonical_v1);
let mut v1_buffer = WalBuffer::new();
for (key, value) in map() {
v1_buffer.put(&key, value.unwrap_or_default());
}
let v1_root = v1_buffer.commit(base, &mut store, TreePolicy::V1_DEFAULT)?;
assert_eq!(v1_root, canonical_v1);
Ok(())
}
fn commit_branch_root(policy: TreePolicy) -> Result<Hash, Box<dyn Error>> {
let mut store = MemoryStore::new();
let base = empty_root(&mut store)?;
let registry = BranchRegistry::new();
let dir = tempfile::tempdir()?;
let mut refs = BranchRefStore::open(dir.path())?;
let branch = create_branch("wip", [(DEFAULT_SHARD_ID, base)], &mut refs, ®istry, 10)?;
for (key, value) in map() {
branch.put(DEFAULT_SHARD_ID, &key, value.unwrap_or_default())?;
}
let outcome = commit_branch(
&branch,
&mut store,
®istry,
CommitRequest {
durability: CommitDurability::Volatile,
extra_parents: &[],
timestamp: 1,
},
policy,
)?;
outcome
.heads
.into_iter()
.find(|(id, _)| *id == DEFAULT_SHARD_ID)
.map(|(_, head)| head)
.ok_or_else(|| "committed shard head absent".into())
}
#[test]
fn branch_commit_dispatches_v2() -> TestResult {
let mut reference = MemoryStore::new();
let canonical_v2 = canonical(&mut reference, v2_policy())?;
let canonical_v1 = canonical(&mut reference, TreePolicy::V1_DEFAULT)?;
assert_eq!(commit_branch_root(v2_policy())?, canonical_v2);
assert_eq!(commit_branch_root(TreePolicy::V1_DEFAULT)?, canonical_v1);
assert_ne!(canonical_v2, canonical_v1);
Ok(())
}
fn merge_fixture(store: &mut MemoryStore) -> Result<(Hash, Hash, Hash, Hash), Box<dyn Error>> {
let base = empty_root(store)?;
let head: Vec<_> = map().into_iter().take(2).collect();
let full = map();
let parent = batch_mutate(store, base, head.as_slice(), v2_policy())?;
let ancestor = parent;
let branch = batch_mutate(store, base, full.as_slice(), v2_policy())?;
let canonical_v2 = canonical(store, v2_policy())?;
Ok((ancestor, parent, branch, canonical_v2))
}
#[test]
fn branch_merge_dispatches_v2() -> TestResult {
let mut store = MemoryStore::new();
let (ancestor, parent, branch, canonical_v2) = merge_fixture(&mut store)?;
let report_v2 = merge_with_report(
&mut store,
parent,
branch,
ancestor,
&ConflictPolicy::Lww,
v2_policy(),
)?;
assert_eq!(
report_v2.merged_root, canonical_v2,
"branch merge must build additions under v2"
);
let report_v1 = merge_with_report(
&mut store,
parent,
branch,
ancestor,
&ConflictPolicy::Lww,
TreePolicy::V1_DEFAULT,
)?;
assert_ne!(
report_v1.merged_root, canonical_v2,
"under v1 the merge must NOT reach the v2 root"
);
Ok(())
}
#[test]
fn sync_merge_dispatches_v2() -> TestResult {
let mut store = MemoryStore::new();
let (ancestor, parent, branch, canonical_v2) = merge_fixture(&mut store)?;
let result = merge_synced_roots(
&mut store,
DEFAULT_SHARD_ID,
SyncMergeRoots::new(parent, branch, ancestor),
&ConflictPolicy::Lww,
v2_policy(),
)?;
assert_eq!(
result.merged_root, canonical_v2,
"sync merge must thread v2 through to the branch merge engine"
);
Ok(())
}
fn stamp(counter: u64, node: &str, seq: u64) -> Stamp {
Stamp::new(Ballot::new(counter, SyncNodeId::new(node)), seq)
}
fn stamped_tree(
store: &mut MemoryStore,
entries: &[(&[u8], Vec<u8>)],
policy: TreePolicy,
) -> Result<Hash, Box<dyn Error>> {
let base = empty_root(store)?;
let mutations: Vec<(Vec<u8>, Option<Vec<u8>>)> = entries
.iter()
.map(|(key, bytes)| (key.to_vec(), Some(bytes.clone())))
.collect();
Ok(batch_mutate(store, base, mutations.as_slice(), policy)?)
}
#[test]
fn handoff_merge_dispatches_v2() -> TestResult {
let a = [(
b"k00".as_slice(),
encode_stamped(oversized(0), stamp(1, "a", 1), None),
)];
let b = [(
b"k01".as_slice(),
encode_stamped(oversized(1), stamp(1, "b", 1), None),
)];
let mut store = MemoryStore::new();
let root_a = stamped_tree(&mut store, &a, v2_policy())?;
let root_b = stamped_tree(&mut store, &b, v2_policy())?;
let present = || -> Box<dyn Error> { "union of two present roots".into() };
let union_v2 = merge_committed_union(Some(root_a), Some(root_b), &mut store, v2_policy())?
.ok_or_else(present)?;
let union_v1 = merge_committed_union(
Some(root_a),
Some(root_b),
&mut store,
TreePolicy::V1_DEFAULT,
)?
.ok_or_else(present)?;
assert_ne!(
union_v2, union_v1,
"handoff union must build under the passed policy, not a v1 literal"
);
let union_v2_again =
merge_committed_union(Some(root_a), Some(root_b), &mut store, v2_policy())?
.ok_or_else(present)?;
assert_eq!(union_v2, union_v2_again);
Ok(())
}
#[test]
fn mixed_policy_regression_same_map_same_root_across_paths() -> TestResult {
let mut store = MemoryStore::new();
let via_batch = canonical(&mut store, v2_policy())?;
let base = empty_root(&mut store)?;
let mut buffer = WalBuffer::new();
for (key, value) in map() {
buffer.put(&key, value.unwrap_or_default());
}
let via_wal = buffer.commit(base, &mut store, v2_policy())?;
let via_branch = commit_branch_root(v2_policy())?;
assert_eq!(
via_batch, via_wal,
"batch vs WAL-buffer path diverged under v2"
);
assert_eq!(
via_batch, via_branch,
"batch vs branch-commit path diverged under v2"
);
Ok(())
}
#[test]
fn formula_threshold_equality_isolates_at_target() {
let target = 48u64;
let policy = TreePolicy::v2(target, target);
let key = b"kk"; let value_len = 30usize; assert!(
policy.leaf_boundary_before(key.len(), value_len),
"an at-target entry must isolate (boundary_before)"
);
assert!(
policy.leaf_boundary_after(key, value_len),
"an at-target entry closes its chunk (boundary_after, P=1)"
);
assert!(
!policy.leaf_boundary_before(key.len(), value_len - 1),
"a sub-target entry must NOT be force-isolated"
);
}
#[test]
fn formula_saturating_size_isolates_without_panic() {
let policy = TreePolicy::v2(64 * 1024, 48 * 1024);
assert!(policy.leaf_boundary_before(usize::MAX, usize::MAX));
assert!(policy.leaf_boundary_after(b"k", usize::MAX));
}