#![cfg(feature = "git")]
mod common;
use prollytree::diff::TakeSourceResolver;
use prollytree::git::versioned_store::{
GitVersionedKvStore, HistoricalAccess, HistoricalCommitAccess,
};
#[test]
fn test_full_branching_lifecycle() {
let (_temp, dataset) = common::setup_repo_and_dataset();
let mut store = GitVersionedKvStore::<32>::init(&dataset).unwrap();
store
.insert(b"main_k".to_vec(), b"main_v".to_vec())
.unwrap();
store.commit("main data").unwrap();
store.create_branch("feature").unwrap();
store.checkout("feature").unwrap();
store
.insert(b"feat_k".to_vec(), b"feat_v".to_vec())
.unwrap();
store.commit("feature data").unwrap();
assert!(store.get(b"main_k").is_some());
assert!(store.get(b"feat_k").is_some());
store.checkout("main").unwrap();
assert!(store.get(b"main_k").is_some());
assert!(
store.get(b"feat_k").is_none(),
"main should not have feature-only key"
);
std::mem::forget(_temp);
}
#[test]
fn test_multiple_branches_diverge_and_converge() {
let (_temp, dataset) = common::setup_repo_and_dataset();
let mut store = GitVersionedKvStore::<32>::init(&dataset).unwrap();
store.insert(b"base".to_vec(), b"val".to_vec()).unwrap();
store.commit("base").unwrap();
for i in 0..3 {
let branch = format!("branch{i}");
store.create_branch(&branch).unwrap();
store.checkout(&branch).unwrap();
store
.insert(
format!("b{i}_key").into_bytes(),
format!("b{i}_val").into_bytes(),
)
.unwrap();
store.commit(&format!("branch {i} commit")).unwrap();
store.checkout("main").unwrap();
}
for i in 0..3 {
let branch = format!("branch{i}");
store
.merge(&branch, &TakeSourceResolver)
.expect(&format!("merge {branch}"));
}
assert!(store.get(b"base").is_some());
for i in 0..3 {
assert!(
store.get(format!("b{i}_key").as_bytes()).is_some(),
"b{i}_key missing after merge"
);
}
std::mem::forget(_temp);
}
#[test]
fn test_history_tracks_key_across_commits() {
let (_temp, dataset) = common::setup_repo_and_dataset();
let mut store = GitVersionedKvStore::<32>::init(&dataset).unwrap();
for i in 0..5 {
store
.insert(b"tracked".to_vec(), format!("v{i}").into_bytes())
.unwrap();
store.commit(&format!("update {i}")).unwrap();
}
let history = store.get_commits_for_key(b"tracked").unwrap();
assert!(
history.len() >= 5,
"expected at least 5 commits for tracked key, got {}",
history.len()
);
std::mem::forget(_temp);
}
#[test]
fn test_get_keys_at_historical_ref() {
let (_temp, dataset) = common::setup_repo_and_dataset();
let mut store = GitVersionedKvStore::<32>::init(&dataset).unwrap();
store.insert(b"A".to_vec(), b"a1".to_vec()).unwrap();
let c1 = store.commit("commit 1").unwrap();
store.insert(b"B".to_vec(), b"b1".to_vec()).unwrap();
let c2 = store.commit("commit 2").unwrap();
store.insert(b"C".to_vec(), b"c1".to_vec()).unwrap();
store.commit("commit 3").unwrap();
let keys_c1 = store.get_keys_at_ref(&c1.to_string()).unwrap();
assert!(keys_c1.contains_key(b"A".as_ref()));
assert!(
!keys_c1.contains_key(b"B".as_ref()),
"B should not exist at c1"
);
let keys_c2 = store.get_keys_at_ref(&c2.to_string()).unwrap();
assert!(keys_c2.contains_key(b"A".as_ref()));
assert!(keys_c2.contains_key(b"B".as_ref()));
assert!(
!keys_c2.contains_key(b"C".as_ref()),
"C should not exist at c2"
);
std::mem::forget(_temp);
}
#[test]
fn test_open_after_close_preserves_state() {
let (_temp, dataset) = common::setup_repo_and_dataset();
{
let mut store = GitVersionedKvStore::<32>::init(&dataset).unwrap();
for i in 0..20 {
store
.insert(
format!("pk{i:03}").into_bytes(),
format!("pv{i:03}").into_bytes(),
)
.unwrap();
}
store.commit("persist data").unwrap();
}
{
let store = GitVersionedKvStore::<32>::open(&dataset).unwrap();
for i in 0..20 {
assert_eq!(
store.get(format!("pk{i:03}").as_bytes()),
Some(format!("pv{i:03}").into_bytes()),
"pk{i:03} should persist"
);
}
let log = store.log().unwrap();
assert!(log.len() >= 2, "should have initial + persist commits");
}
}