use super::{create_branch, fork_at, fork_from, open_branch, remove_branch};
use crate::branch::commit::{BranchCommitError, CommitDurability, CommitRequest, commit_branch};
use crate::branch::fork::fork_registered;
use crate::branch::handle::DEFAULT_SHARD_ID;
use crate::branch::refstore::{BranchRefError, BranchRefStore};
use crate::branch::registry::BranchRegistry;
use crate::branch::snapshot::{SnapshotError, SnapshotRegistry};
use crate::store::MemoryStore;
use crate::tree::{Hash, LeafNode, Node, TreeError, insert};
type TestResult = Result<(), BranchCommitError>;
fn hash(byte: u8) -> Hash {
Hash::from_bytes([byte; 32])
}
fn build_tree(store: &mut MemoryStore, entries: &[(&[u8], &[u8])]) -> Result<Hash, TreeError> {
let leaf = LeafNode::new(Vec::new())?;
let mut root = store.put(&Node::Leaf(leaf));
for (key, value) in entries {
root = insert(store, root, key, value)?;
}
Ok(root)
}
fn tempdir() -> Result<tempfile::TempDir, BranchCommitError> {
tempfile::tempdir().map_err(|error| BranchCommitError::Ref(BranchRefError::Io(error)))
}
fn snapshot_error(error: &SnapshotError) -> BranchCommitError {
BranchCommitError::Barrier(error.to_string())
}
#[test]
fn fork_from_anchors_at_committed_heads_buffer_invisible() -> TestResult {
let mut store = MemoryStore::new();
let root = build_tree(&mut store, &[(b"key", b"committed")])?;
let registry = BranchRegistry::new();
let parent = fork_registered(root, ®istry);
parent.put(DEFAULT_SHARD_ID, b"key", b"uncommitted")?;
let child = fork_from(&parent, ®istry)?;
assert_eq!(child.fork_point(), root);
assert_eq!(child.current_root(), root);
assert_eq!(
child.get(DEFAULT_SHARD_ID, &store, b"key")?,
Some(b"committed".to_vec())
);
drop(parent);
assert!(registry.live_roots().contains(&root));
drop(child);
assert!(registry.live_roots().is_empty());
Ok(())
}
#[test]
fn fork_from_sees_parent_committed_advances() -> TestResult {
let mut store = MemoryStore::new();
let root = build_tree(&mut store, &[(b"base", b"tree")])?;
let registry = BranchRegistry::new();
let dir = tempdir()?;
let mut refs = BranchRefStore::open(dir.path())?;
let parent = create_branch(
"parent",
[(DEFAULT_SHARD_ID, root)],
&mut refs,
®istry,
10,
)?;
parent.put(DEFAULT_SHARD_ID, b"landed", b"yes")?;
commit_branch(
&parent,
&mut store,
®istry,
CommitRequest {
durability: CommitDurability::Volatile,
extra_parents: &[],
timestamp: 20,
},
)?;
let committed = parent.current_root();
parent.put(DEFAULT_SHARD_ID, b"pending", b"no")?;
let child = fork_from(&parent, ®istry)?;
assert_eq!(child.fork_point(), committed);
assert_eq!(
child.get(DEFAULT_SHARD_ID, &store, b"landed")?,
Some(b"yes".to_vec())
);
assert_eq!(child.get(DEFAULT_SHARD_ID, &store, b"pending")?, None);
Ok(())
}
#[test]
fn create_branch_pins_and_installs_seq_zero_record() -> TestResult {
let registry = BranchRegistry::new();
let dir = tempdir()?;
let mut refs = BranchRefStore::open(dir.path())?;
let branch = create_branch(
"fresh",
[(0, hash(1)), (3, hash(2))],
&mut refs,
®istry,
42,
)?;
assert!(branch.is_named());
let record = refs
.get("fresh")
.ok_or_else(|| BranchCommitError::Ref(BranchRefError::BranchRemoved("fresh".into())))?;
assert_eq!(record.created, 42);
assert_eq!(record.seq, 0);
assert_eq!(record.shards[0].fork_anchor, hash(1));
assert_eq!(record.shards[0].head, hash(1));
assert_eq!(record.shards[1].fork_anchor, hash(2));
assert_eq!(record.shards[1].head, hash(2));
assert!(record.parents.is_empty());
let live = registry.live_roots();
assert!(live.contains(&hash(1)));
assert!(live.contains(&hash(2)));
drop(branch);
assert!(registry.live_roots().is_empty());
assert!(refs.protected_roots().contains(&hash(1)));
Ok(())
}
#[test]
fn duplicate_create_is_typed_and_releases_its_pins() -> TestResult {
let registry = BranchRegistry::new();
let dir = tempdir()?;
let mut refs = BranchRefStore::open(dir.path())?;
let _first = create_branch("dup", [(0, hash(1))], &mut refs, ®istry, 10)?;
let live_before = registry.live_roots();
let result = create_branch("dup", [(0, hash(2))], &mut refs, ®istry, 20);
assert!(matches!(
result,
Err(BranchCommitError::Ref(BranchRefError::DuplicateBranch(name))) if name == "dup"
));
assert_eq!(registry.live_roots(), live_before);
Ok(())
}
#[test]
fn open_branch_restores_anchor_as_fork_point() -> TestResult {
let mut store = MemoryStore::new();
let root = build_tree(&mut store, &[(b"base", b"tree")])?;
let registry = BranchRegistry::new();
let dir = tempdir()?;
let mut refs = BranchRefStore::open(dir.path())?;
let head = {
let branch = create_branch("sess", [(DEFAULT_SHARD_ID, root)], &mut refs, ®istry, 10)?;
branch.put(DEFAULT_SHARD_ID, b"key", b"advanced")?;
commit_branch(
&branch,
&mut store,
®istry,
CommitRequest {
durability: CommitDurability::Durable { refs: &mut refs },
extra_parents: &[],
timestamp: 20,
},
)?;
branch.current_root()
};
assert!(registry.live_roots().is_empty());
let refs = BranchRefStore::open(dir.path())?;
let reopened = open_branch("sess", &refs, ®istry)?;
assert!(reopened.is_named());
assert_eq!(
reopened.fork_point(),
root,
"the recorded anchor — the merge ancestor — must survive reopen"
);
assert_eq!(reopened.current_root(), head);
let live = registry.live_roots();
assert!(live.contains(&root));
assert!(live.contains(&head));
let mut refs = BranchRefStore::open(dir.path())?;
reopened.put(DEFAULT_SHARD_ID, b"key2", b"next")?;
let outcome = commit_branch(
&reopened,
&mut store,
®istry,
CommitRequest {
durability: CommitDurability::Durable { refs: &mut refs },
extra_parents: &[],
timestamp: 30,
},
)?;
assert_eq!(outcome.seq, Some(2));
Ok(())
}
#[test]
fn open_unknown_branch_is_typed() -> TestResult {
let registry = BranchRegistry::new();
let dir = tempdir()?;
let refs = BranchRefStore::open(dir.path())?;
let result = open_branch("never", &refs, ®istry);
assert!(matches!(
result,
Err(BranchCommitError::Ref(BranchRefError::BranchRemoved(name))) if name == "never"
));
Ok(())
}
#[test]
fn remove_branch_returns_record_and_leaves_unknown_names_none() -> TestResult {
let registry = BranchRegistry::new();
let dir = tempdir()?;
let mut refs = BranchRefStore::open(dir.path())?;
create_branch("done", [(0, hash(1))], &mut refs, ®istry, 10)?;
let removed = remove_branch("done", &mut refs).map_err(BranchCommitError::Ref)?;
assert!(matches!(removed, Some(record) if record.name == "done"));
let again = remove_branch("done", &mut refs).map_err(BranchCommitError::Ref)?;
assert!(again.is_none());
Ok(())
}
#[test]
fn fork_at_unprotected_hash_is_hard_refused() -> TestResult {
let registry = BranchRegistry::new();
let dir = tempdir()?;
let refs = BranchRefStore::open(dir.path())?;
let snapshots = SnapshotRegistry::new();
let rogue = hash(9);
let result = fork_at(rogue, ®istry, &refs, &snapshots);
let Err(error) = result else {
return Err(BranchCommitError::UnprotectedAnchor { root: rogue });
};
assert!(matches!(
error,
BranchCommitError::UnprotectedAnchor { root } if root == rogue
));
assert!(error.to_string().contains("name a snapshot"));
assert!(registry.live_roots().is_empty());
Ok(())
}
#[test]
fn fork_at_accepts_each_protection_source() -> TestResult {
let registry = BranchRegistry::new();
let dir = tempdir()?;
let mut refs = BranchRefStore::open(dir.path())?;
let mut snapshots = SnapshotRegistry::new();
snapshots
.name("pin", hash(1))
.map_err(|error| snapshot_error(&error))?;
let from_snapshot = fork_at(hash(1), ®istry, &refs, &snapshots)?;
assert_eq!(from_snapshot.fork_point(), hash(1));
let named = create_branch("layer", [(0, hash(2))], &mut refs, ®istry, 10)?;
let from_record = fork_at(hash(2), ®istry, &refs, &snapshots)?;
assert_eq!(from_record.fork_point(), hash(2));
let holder = fork_registered(hash(3), ®istry);
let from_live = fork_at(hash(3), ®istry, &refs, &snapshots)?;
drop(holder);
assert!(registry.live_roots().contains(&hash(3)));
drop((named, from_snapshot, from_record, from_live));
Ok(())
}
#[test]
fn fork_from_refuses_unregistered_parent() -> TestResult {
let mut store = MemoryStore::new();
let root = build_tree(&mut store, &[(b"key", b"v")])?;
let registry = BranchRegistry::new();
let parent = crate::branch::handle::BranchHandle::new(root);
let result = fork_from(&parent, ®istry);
assert!(
matches!(
result,
Err(crate::branch::handle::BranchError::UnregisteredParent)
),
"fork_from must refuse an unregistered parent"
);
Ok(())
}
#[test]
fn durable_noop_commit_still_runs_cas() -> TestResult {
let mut store = MemoryStore::new();
let root = build_tree(&mut store, &[(b"base", b"tree")])?;
let registry = BranchRegistry::new();
let dir = tempdir()?;
let mut refs = BranchRefStore::open(dir.path())?;
let branch = create_branch("gone", [(DEFAULT_SHARD_ID, root)], &mut refs, ®istry, 10)?;
remove_branch("gone", &mut refs).map_err(BranchCommitError::Ref)?;
let result = commit_branch(
&branch,
&mut store,
®istry,
CommitRequest {
durability: CommitDurability::Durable { refs: &mut refs },
extra_parents: &[],
timestamp: 20,
},
);
assert!(
matches!(
result,
Err(BranchCommitError::Ref(BranchRefError::BranchRemoved(ref name))) if name == "gone"
),
"a durable no-op on a removed branch must surface BranchRemoved"
);
Ok(())
}