#[cfg(test)]
mod proof_tests {
use crate::git::versioned_store::{GitVersionedKvStore, HistoricalAccess};
use tempfile::TempDir;
struct CwdGuard {
original: std::path::PathBuf,
_lock: std::sync::MutexGuard<'static, ()>,
}
impl CwdGuard {
fn set(path: &std::path::Path) -> Self {
let lock = crate::git::versioned_store::cwd_lock()
.lock()
.expect("CWD mutex poisoned");
let original = std::env::current_dir().expect("Failed to get current dir");
std::env::set_current_dir(path).expect("Failed to change directory");
Self {
original,
_lock: lock,
}
}
}
impl Drop for CwdGuard {
fn drop(&mut self) {
let _ = std::env::set_current_dir(&self.original);
}
}
#[test]
fn test_versioned_store_proof_methods() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let repo_path = temp_dir.path().to_str().unwrap();
std::process::Command::new("git")
.args(["init"])
.current_dir(repo_path)
.output()
.expect("Failed to initialize git repo");
std::process::Command::new("git")
.args(["config", "user.name", "Test User"])
.current_dir(repo_path)
.output()
.expect("Failed to set git user name");
std::process::Command::new("git")
.args(["config", "user.email", "test@example.com"])
.current_dir(repo_path)
.output()
.expect("Failed to set git user email");
let dataset_path = temp_dir.path().join("dataset");
std::fs::create_dir(&dataset_path).expect("Failed to create dataset directory");
let _cwd_guard = CwdGuard::set(&dataset_path);
let mut store =
GitVersionedKvStore::<32>::init(&dataset_path).expect("Failed to initialize store");
let key = b"proof_test_key".to_vec();
let value = b"proof_test_value".to_vec();
store
.insert(key.clone(), value.clone())
.expect("Failed to insert");
store
.commit("Add test data for proof")
.expect("Failed to commit");
let proof = store.generate_proof(&key);
assert!(store.verify(proof.clone(), &key, Some(&value)));
assert!(store.verify(proof.clone(), &key, None));
let wrong_value = b"wrong_value".to_vec();
assert!(!store.verify(proof.clone(), &key, Some(&wrong_value)));
}
#[test]
fn test_get_keys_at_ref() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let repo_path = temp_dir.path().to_str().unwrap();
std::process::Command::new("git")
.args(["init"])
.current_dir(repo_path)
.output()
.expect("Failed to initialize git repo");
std::process::Command::new("git")
.args(["config", "user.name", "Test User"])
.current_dir(repo_path)
.output()
.expect("Failed to set git user name");
std::process::Command::new("git")
.args(["config", "user.email", "test@example.com"])
.current_dir(repo_path)
.output()
.expect("Failed to set git user email");
let dataset_path = temp_dir.path().join("dataset");
std::fs::create_dir(&dataset_path).expect("Failed to create dataset directory");
let _cwd_guard = CwdGuard::set(&dataset_path);
let mut store =
GitVersionedKvStore::<32>::init(&dataset_path).expect("Failed to initialize store");
store
.insert(b"key1".to_vec(), b"value1".to_vec())
.expect("Failed to insert key1");
store
.insert(b"key2".to_vec(), b"value2".to_vec())
.expect("Failed to insert key2");
let commit1 = store.commit("Initial commit").expect("Failed to commit");
let keys_at_head = store
.get_keys_at_ref("HEAD")
.expect("Failed to get keys at HEAD");
assert_eq!(keys_at_head.len(), 2);
assert_eq!(
keys_at_head.get(&b"key1".to_vec()),
Some(&b"value1".to_vec())
);
assert_eq!(
keys_at_head.get(&b"key2".to_vec()),
Some(&b"value2".to_vec())
);
store
.insert(b"key3".to_vec(), b"value3".to_vec())
.expect("Failed to insert key3");
store
.update(b"key1".to_vec(), b"updated1".to_vec())
.expect("Failed to update key1");
let _commit2 = store.commit("Second commit").expect("Failed to commit");
let keys_at_commit1 = store
.get_keys_at_ref(&commit1.to_hex().to_string())
.expect("Failed to get keys at commit1");
assert_eq!(keys_at_commit1.len(), 2);
assert_eq!(
keys_at_commit1.get(&b"key1".to_vec()),
Some(&b"value1".to_vec())
);
assert_eq!(
keys_at_commit1.get(&b"key2".to_vec()),
Some(&b"value2".to_vec())
);
assert!(!keys_at_commit1.contains_key(&b"key3".to_vec()));
let keys_at_current_head = store
.get_keys_at_ref("HEAD")
.expect("Failed to get keys at current HEAD");
assert_eq!(keys_at_current_head.len(), 3);
assert_eq!(
keys_at_current_head.get(&b"key1".to_vec()),
Some(&b"updated1".to_vec())
);
assert_eq!(
keys_at_current_head.get(&b"key2".to_vec()),
Some(&b"value2".to_vec())
);
assert_eq!(
keys_at_current_head.get(&b"key3".to_vec()),
Some(&b"value3".to_vec())
);
}
}
#[cfg(test)]
mod tests {
use crate::git::types::DiffOperation;
use crate::git::versioned_store::{
FileVersionedKvStore, GitVersionedKvStore, HistoricalAccess, HistoricalCommitAccess,
InMemoryVersionedKvStore, ThreadSafeGitVersionedKvStore,
};
#[cfg(feature = "rocksdb_storage")]
use crate::git::versioned_store::RocksDBVersionedKvStore;
use tempfile::TempDir;
struct CwdGuard {
original: std::path::PathBuf,
_lock: std::sync::MutexGuard<'static, ()>,
}
impl CwdGuard {
fn set(path: &std::path::Path) -> Self {
let lock = crate::git::versioned_store::cwd_lock()
.lock()
.expect("CWD mutex poisoned");
let original = std::env::current_dir().expect("Failed to get current dir");
std::env::set_current_dir(path).expect("Failed to change directory");
Self {
original,
_lock: lock,
}
}
}
impl Drop for CwdGuard {
fn drop(&mut self) {
let _ = std::env::set_current_dir(&self.original);
}
}
#[test]
fn test_versioned_store_init() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let store = GitVersionedKvStore::<32>::init(&dataset_dir);
assert!(store.is_ok());
}
#[test]
fn test_basic_kv_operations() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
assert_eq!(store.get(b"key1"), Some(b"value1".to_vec()));
store
.update(b"key1".to_vec(), b"new_value1".to_vec())
.unwrap();
assert_eq!(store.get(b"key1"), Some(b"new_value1".to_vec()));
store.delete(b"key1").unwrap();
assert_eq!(store.get(b"key1"), None);
}
#[test]
fn test_commit_workflow() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
store.insert(b"key2".to_vec(), b"value2".to_vec()).unwrap();
let status = store.status();
assert_eq!(status.len(), 2);
let commit_id = store.commit("Add initial data").unwrap();
assert!(!commit_id.is_null());
let status = store.status();
assert_eq!(status.len(), 0);
}
#[test]
fn test_single_commit_behavior() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
let log_output = std::process::Command::new("git")
.args(&["log", "--oneline"])
.current_dir(temp_dir.path())
.output()
.unwrap();
let initial_commits = String::from_utf8_lossy(&log_output.stdout).lines().count();
store
.insert(b"test_key".to_vec(), b"test_value".to_vec())
.unwrap();
store.commit("Test single commit").unwrap();
let log_output = std::process::Command::new("git")
.args(&["log", "--oneline"])
.current_dir(temp_dir.path())
.output()
.unwrap();
let final_commits = String::from_utf8_lossy(&log_output.stdout).lines().count();
assert_eq!(
final_commits,
initial_commits + 1,
"Expected exactly one new commit, but got {} new commits",
final_commits - initial_commits
);
let config_path = dataset_dir.join("prolly_config_tree_config");
let mapping_path = dataset_dir.join("prolly_hash_mappings");
assert!(
config_path.exists(),
"prolly_config_tree_config should exist"
);
assert!(mapping_path.exists(), "prolly_hash_mappings should exist");
}
#[test]
fn test_diff_between_commits() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
store.insert(b"key2".to_vec(), b"value2".to_vec()).unwrap();
let commit1 = store.commit("Initial data").unwrap();
store
.update(b"key1".to_vec(), b"value1_modified".to_vec())
.unwrap();
store.insert(b"key3".to_vec(), b"value3".to_vec()).unwrap();
store.delete(b"key2").unwrap();
let commit2 = store.commit("Modify data").unwrap();
let diffs = store
.diff(&commit1.to_hex().to_string(), &commit2.to_hex().to_string())
.unwrap();
println!("Diffs found: {}", diffs.len());
for diff in &diffs {
println!(
" {:?}: {:?}",
String::from_utf8_lossy(&diff.key),
diff.operation
);
}
assert_eq!(diffs.len(), 3);
assert_eq!(diffs[0].key, b"key1");
match &diffs[0].operation {
DiffOperation::Modified { old, new } => {
assert_eq!(old, b"value1");
assert_eq!(new, b"value1_modified");
}
_ => panic!("Expected key1 to be modified"),
}
assert_eq!(diffs[1].key, b"key2");
match &diffs[1].operation {
DiffOperation::Removed(value) => {
assert_eq!(value, b"value2");
}
_ => panic!("Expected key2 to be removed"),
}
assert_eq!(diffs[2].key, b"key3");
match &diffs[2].operation {
DiffOperation::Added(value) => {
assert_eq!(value, b"value3");
}
_ => panic!("Expected key3 to be added"),
}
}
#[test]
fn test_diff_between_branches() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
store.insert(b"key2".to_vec(), b"value2".to_vec()).unwrap();
store.commit("Initial data").unwrap();
store.create_branch("feature").unwrap();
store
.update(b"key1".to_vec(), b"value1_feature".to_vec())
.unwrap();
store.insert(b"key3".to_vec(), b"value3".to_vec()).unwrap();
store.commit("Feature changes").unwrap();
let diffs = store.diff("main", "feature").unwrap();
assert_eq!(diffs.len(), 2);
assert_eq!(diffs[0].key, b"key1");
match &diffs[0].operation {
DiffOperation::Modified { old, new } => {
assert_eq!(old, b"value1");
assert_eq!(new, b"value1_feature");
}
_ => panic!("Expected key1 to be modified"),
}
assert_eq!(diffs[1].key, b"key3");
match &diffs[1].operation {
DiffOperation::Added(value) => {
assert_eq!(value, b"value3");
}
_ => panic!("Expected key3 to be added"),
}
}
#[test]
fn test_init_with_existing_store() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store
.insert(b"test_key".to_vec(), b"test_value".to_vec())
.unwrap();
store.commit("Test data").unwrap();
drop(store);
let store2 = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
assert_eq!(
store2.get(b"test_key"),
Some(b"test_value".to_vec()),
"Data should still exist after second init"
);
let config_path = dataset_dir.join("prolly_config_tree_config");
let mapping_path = dataset_dir.join("prolly_hash_mappings");
assert!(config_path.exists(), "Config file should exist");
assert!(mapping_path.exists(), "Mapping file should exist");
}
#[test]
fn test_diff_with_no_changes() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
let commit = store.commit("Initial data").unwrap();
let diffs = store
.diff(&commit.to_hex().to_string(), &commit.to_hex().to_string())
.unwrap();
assert_eq!(diffs.len(), 0);
}
#[test]
fn test_diff_with_inmemory_storage() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = InMemoryVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
store.insert(b"key2".to_vec(), b"value2".to_vec()).unwrap();
let commit1 = store.commit("Initial data").unwrap();
store
.update(b"key1".to_vec(), b"updated_value1".to_vec())
.unwrap();
store.insert(b"key3".to_vec(), b"value3".to_vec()).unwrap();
let commit2 = store.commit("Update data").unwrap();
let diffs = store
.diff(&commit1.to_hex().to_string(), &commit2.to_hex().to_string())
.unwrap();
assert_eq!(diffs.len(), 2);
let head_diffs = store.diff(&commit1.to_hex().to_string(), "HEAD").unwrap();
assert_eq!(head_diffs.len(), 2);
let same_diffs = store
.diff(&commit1.to_hex().to_string(), &commit1.to_hex().to_string())
.unwrap();
assert_eq!(same_diffs.len(), 0);
}
#[test]
fn test_get_commits_for_key() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
store.insert(b"key2".to_vec(), b"value2".to_vec()).unwrap();
let commit1 = store.commit("Add key1 and key2").unwrap();
store
.update(b"key1".to_vec(), b"value1_modified".to_vec())
.unwrap();
let commit2 = store.commit("Modify key1").unwrap();
store.insert(b"key3".to_vec(), b"value3".to_vec()).unwrap();
let commit3 = store.commit("Add key3").unwrap();
store.delete(b"key1").unwrap();
let commit4 = store.commit("Delete key1").unwrap();
let key1_commits = store.get_commits(b"key1").unwrap();
eprintln!("key1_commits found: {}", key1_commits.len());
for (i, commit) in key1_commits.iter().enumerate() {
eprintln!(" [{}] {} - {}", i, commit.id, commit.message.trim());
}
eprintln!("Expected commits:");
eprintln!(" commit4 (delete): {}", commit4);
eprintln!(" commit2 (modify): {}", commit2);
eprintln!(" commit1 (add): {}", commit1);
assert_eq!(key1_commits.len(), 3);
assert_eq!(key1_commits[0].id, commit4); assert_eq!(key1_commits[1].id, commit2); assert_eq!(key1_commits[2].id, commit1);
let key2_commits = store.get_commits(b"key2").unwrap();
assert_eq!(key2_commits.len(), 1);
assert_eq!(key2_commits[0].id, commit1);
let key3_commits = store.get_commits(b"key3").unwrap();
assert_eq!(key3_commits.len(), 1);
assert_eq!(key3_commits[0].id, commit3);
let nonexistent_commits = store.get_commits(b"nonexistent").unwrap();
assert_eq!(nonexistent_commits.len(), 0);
}
#[test]
fn test_get_commits_with_repeated_changes() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key".to_vec(), b"value1".to_vec()).unwrap();
let commit1 = store.commit("Add key with value1").unwrap();
store.update(b"key".to_vec(), b"value1".to_vec()).unwrap();
let _commit2 = store.commit("Update key to same value").unwrap();
store.update(b"key".to_vec(), b"value2".to_vec()).unwrap();
let commit3 = store.commit("Change key to value2").unwrap();
store.update(b"key".to_vec(), b"value1".to_vec()).unwrap();
let commit4 = store.commit("Change key back to value1").unwrap();
let key_commits = store.get_commits(b"key").unwrap();
assert_eq!(key_commits.len(), 3);
assert_eq!(key_commits[0].id, commit4); assert_eq!(key_commits[1].id, commit3); assert_eq!(key_commits[2].id, commit1); }
#[test]
fn test_historical_access_non_git_storages() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
{
let mut store = InMemoryVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
store.insert(b"key2".to_vec(), b"value2".to_vec()).unwrap();
let commit_id = store.commit("Initial data").unwrap();
let keys_at_head = store.get_keys_at_ref("HEAD").unwrap();
assert_eq!(keys_at_head.len(), 2);
assert!(keys_at_head.contains_key(&b"key1".to_vec()));
assert!(keys_at_head.contains_key(&b"key2".to_vec()));
let keys_at_commit = store
.get_keys_at_ref(&commit_id.to_hex().to_string())
.unwrap();
assert_eq!(keys_at_commit.len(), 2);
let commit_history = store.get_commit_history().unwrap();
assert!(!commit_history.is_empty());
let key1_commits = store.get_commits(b"key1").unwrap();
assert!(!key1_commits.is_empty());
}
{
let mut store = FileVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
store.insert(b"key2".to_vec(), b"value2".to_vec()).unwrap();
let _commit_id = store.commit("Initial data").unwrap();
let keys_at_head = store.get_keys_at_ref("HEAD").unwrap();
assert_eq!(keys_at_head.len(), 2);
assert!(keys_at_head.contains_key(&b"key1".to_vec()));
assert!(keys_at_head.contains_key(&b"key2".to_vec()));
let commit_history = store.get_commit_history().unwrap();
assert!(!commit_history.is_empty());
let key1_commits = store.get_commits(b"key1").unwrap();
assert!(!key1_commits.is_empty());
}
#[cfg(feature = "rocksdb_storage")]
{
let mut store = RocksDBVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
store.insert(b"key2".to_vec(), b"value2".to_vec()).unwrap();
let _commit_id = store.commit("Initial data").unwrap();
let keys_at_head = store.get_keys_at_ref("HEAD").unwrap();
assert_eq!(keys_at_head.len(), 2);
assert!(keys_at_head.contains_key(&b"key1".to_vec()));
assert!(keys_at_head.contains_key(&b"key2".to_vec()));
let commit_history = store.get_commit_history().unwrap();
assert!(!commit_history.is_empty());
let key1_commits = store.get_commits(b"key1").unwrap();
assert!(!key1_commits.is_empty());
}
}
#[test]
fn test_get_commits_complex_multi_branch_scenarios() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store
.insert(b"key1".to_vec(), b"value1_v1".to_vec())
.unwrap();
store
.insert(b"shared_key".to_vec(), b"shared_v1".to_vec())
.unwrap();
let commit1 = store
.commit("Initial commit with key1 and shared_key")
.unwrap();
store
.update(b"key1".to_vec(), b"value1_v2".to_vec())
.unwrap();
store
.insert(b"key2".to_vec(), b"value2_v1".to_vec())
.unwrap();
let commit2 = store.commit("Update key1, add key2").unwrap();
store.create_branch("feature/new-keys").unwrap();
store.checkout("feature/new-keys").unwrap();
store
.update(b"key2".to_vec(), b"value2_branch_v1".to_vec())
.unwrap();
store
.insert(b"key3".to_vec(), b"value3_branch_v1".to_vec())
.unwrap();
store
.update(b"shared_key".to_vec(), b"shared_branch_v1".to_vec())
.unwrap();
let branch_commit1 = store
.commit("Feature branch: modify key2, add key3, update shared_key")
.unwrap();
store
.update(b"key3".to_vec(), b"value3_branch_v2".to_vec())
.unwrap();
let branch_commit2 = store.commit("Feature branch: update key3 again").unwrap();
store.checkout("main").unwrap();
store.delete(b"key2").unwrap();
store
.update(b"shared_key".to_vec(), b"shared_main_v2".to_vec())
.unwrap();
let main_commit3 = store
.commit("Main: delete key2, update shared_key")
.unwrap();
store.create_branch("hotfix/key1-fix").unwrap();
store.checkout("hotfix/key1-fix").unwrap();
store
.update(b"key1".to_vec(), b"value1_hotfixed".to_vec())
.unwrap();
let hotfix_commit = store.commit("Hotfix: critical key1 update").unwrap();
println!("\n=== Testing key1 commits across branches ===");
store.checkout("main").unwrap();
let key1_commits_main = store.get_commits(b"key1").unwrap();
println!("Key1 commits from main branch: {}", key1_commits_main.len());
for (i, commit) in key1_commits_main.iter().enumerate() {
println!(" {}: {} - {}", i, commit.id, commit.message);
}
assert_eq!(key1_commits_main.len(), 2);
assert_eq!(key1_commits_main[0].id, commit2); assert_eq!(key1_commits_main[1].id, commit1);
store.checkout("hotfix/key1-fix").unwrap();
let key1_commits_hotfix = store.get_commits(b"key1").unwrap();
println!(
"Key1 commits from hotfix branch: {}",
key1_commits_hotfix.len()
);
for (i, commit) in key1_commits_hotfix.iter().enumerate() {
println!(" {}: {} - {}", i, commit.id, commit.message);
}
assert_eq!(key1_commits_hotfix.len(), 3);
assert_eq!(key1_commits_hotfix[0].id, hotfix_commit);
assert_eq!(key1_commits_hotfix[1].id, commit2);
assert_eq!(key1_commits_hotfix[2].id, commit1);
println!("\n=== Testing key2 commits across branches ===");
store.checkout("main").unwrap();
let key2_commits_main = store.get_commits(b"key2").unwrap();
println!("Key2 commits from main branch: {}", key2_commits_main.len());
for (i, commit) in key2_commits_main.iter().enumerate() {
println!(" {}: {} - {}", i, commit.id, commit.message);
}
assert_eq!(key2_commits_main.len(), 2);
assert_eq!(key2_commits_main[0].id, main_commit3);
assert_eq!(key2_commits_main[1].id, commit2);
store.checkout("feature/new-keys").unwrap();
let key2_commits_feature = store.get_commits(b"key2").unwrap();
println!(
"Key2 commits from feature branch: {}",
key2_commits_feature.len()
);
for (i, commit) in key2_commits_feature.iter().enumerate() {
println!(" {}: {} - {}", i, commit.id, commit.message);
}
assert_eq!(key2_commits_feature.len(), 2);
assert_eq!(key2_commits_feature[0].id, branch_commit1);
assert_eq!(key2_commits_feature[1].id, commit2);
println!("\n=== Testing key3 commits (feature branch only) ===");
let key3_commits_feature = store.get_commits(b"key3").unwrap();
println!(
"Key3 commits from feature branch: {}",
key3_commits_feature.len()
);
for (i, commit) in key3_commits_feature.iter().enumerate() {
println!(" {}: {} - {}", i, commit.id, commit.message);
}
assert_eq!(key3_commits_feature.len(), 2);
assert_eq!(key3_commits_feature[0].id, branch_commit2);
assert_eq!(key3_commits_feature[1].id, branch_commit1);
store.checkout("main").unwrap();
let keys_at_main_head = store.get_keys_at_ref("HEAD").unwrap();
println!(
"Keys at main HEAD: {:?}",
keys_at_main_head.keys().collect::<Vec<_>>()
);
println!(
"Key3 value at main HEAD: {:?}",
keys_at_main_head.get(&b"key3".to_vec())
);
let key3_commits_main = store.get_commits(b"key3").unwrap();
println!("Key3 commits from main branch: {}", key3_commits_main.len());
for (i, commit) in key3_commits_main.iter().enumerate() {
println!(" {}: {} - {}", i, commit.id, commit.message);
let keys_at_commit = store.collect_keys_at_commit(&commit.id).unwrap();
println!(
" Keys at this commit: {:?}",
keys_at_commit.keys().collect::<Vec<_>>()
);
println!(
" Key3 value at this commit: {:?}",
keys_at_commit.get(&b"key3".to_vec())
);
}
assert!(
!keys_at_main_head.contains_key(&b"key3".to_vec()),
"key3 should not exist at main HEAD"
);
println!("\n=== Testing shared_key commits across branches ===");
let shared_commits_main = store.get_commits(b"shared_key").unwrap();
println!(
"Shared_key commits from main branch: {}",
shared_commits_main.len()
);
for (i, commit) in shared_commits_main.iter().enumerate() {
println!(" {}: {} - {}", i, commit.id, commit.message);
}
assert_eq!(shared_commits_main.len(), 2);
assert_eq!(shared_commits_main[0].id, main_commit3);
assert_eq!(shared_commits_main[1].id, commit1);
store.checkout("feature/new-keys").unwrap();
let shared_commits_feature = store.get_commits(b"shared_key").unwrap();
println!(
"Shared_key commits from feature branch: {}",
shared_commits_feature.len()
);
for (i, commit) in shared_commits_feature.iter().enumerate() {
println!(" {}: {} - {}", i, commit.id, commit.message);
}
assert_eq!(shared_commits_feature.len(), 2);
assert_eq!(shared_commits_feature[0].id, branch_commit1);
assert_eq!(shared_commits_feature[1].id, commit1);
println!("\n=== Multi-branch commit tracking test completed successfully ===");
}
#[test]
fn test_get_commits_merge_scenarios() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store
.insert(b"file1".to_vec(), b"main_content".to_vec())
.unwrap();
store
.insert(b"shared_file".to_vec(), b"original".to_vec())
.unwrap();
let main_commit1 = store.commit("Main: initial files").unwrap();
store.create_branch("feature/enhancement").unwrap();
store.checkout("feature/enhancement").unwrap();
store
.insert(b"new_feature".to_vec(), b"feature_code".to_vec())
.unwrap();
store
.update(b"shared_file".to_vec(), b"feature_modified".to_vec())
.unwrap();
let feature_commit1 = store
.commit("Feature: add new feature and modify shared file")
.unwrap();
store
.update(b"new_feature".to_vec(), b"enhanced_feature_code".to_vec())
.unwrap();
let feature_commit2 = store.commit("Feature: enhance the new feature").unwrap();
store.checkout("main").unwrap();
store
.update(b"file1".to_vec(), b"main_updated_content".to_vec())
.unwrap();
store
.insert(b"main_only".to_vec(), b"main_specific".to_vec())
.unwrap();
let main_commit2 = store
.commit("Main: update file1 and add main-specific file")
.unwrap();
println!("\n=== Testing commits before merge ===");
let feature_commits_from_main = store.get_commits(b"new_feature").unwrap();
assert_eq!(
feature_commits_from_main.len(),
0,
"new_feature should not exist on main branch"
);
store.checkout("feature/enhancement").unwrap();
let feature_commits_from_feature = store.get_commits(b"new_feature").unwrap();
assert_eq!(
feature_commits_from_feature.len(),
2,
"new_feature should have 2 commits on feature branch"
);
assert_eq!(feature_commits_from_feature[0].id, feature_commit2);
assert_eq!(feature_commits_from_feature[1].id, feature_commit1);
let shared_commits_feature = store.get_commits(b"shared_file").unwrap();
assert_eq!(shared_commits_feature.len(), 2);
assert_eq!(shared_commits_feature[0].id, feature_commit1); assert_eq!(shared_commits_feature[1].id, main_commit1);
store.checkout("main").unwrap();
let shared_commits_main = store.get_commits(b"shared_file").unwrap();
assert_eq!(shared_commits_main.len(), 1);
assert_eq!(shared_commits_main[0].id, main_commit1);
let file1_commits_main = store.get_commits(b"file1").unwrap();
assert_eq!(file1_commits_main.len(), 2);
assert_eq!(file1_commits_main[0].id, main_commit2); assert_eq!(file1_commits_main[1].id, main_commit1);
store.checkout("feature/enhancement").unwrap();
let file1_commits_feature = store.get_commits(b"file1").unwrap();
assert_eq!(file1_commits_feature.len(), 1);
assert_eq!(file1_commits_feature[0].id, main_commit1);
println!("=== Merge scenario commit tracking test completed successfully ===");
}
#[test]
fn test_get_commits_key_lifecycle_patterns() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store
.insert(b"lifecycle_key".to_vec(), b"v1".to_vec())
.unwrap();
let create_commit = store.commit("Create lifecycle_key").unwrap();
store
.update(b"lifecycle_key".to_vec(), b"v2".to_vec())
.unwrap();
let update1_commit = store.commit("Update lifecycle_key to v2").unwrap();
store
.update(b"lifecycle_key".to_vec(), b"v3".to_vec())
.unwrap();
let update2_commit = store.commit("Update lifecycle_key to v3").unwrap();
store
.update(b"lifecycle_key".to_vec(), b"v4_final".to_vec())
.unwrap();
let update3_commit = store.commit("Final update of lifecycle_key").unwrap();
store.delete(b"lifecycle_key").unwrap();
let delete_commit = store.commit("Delete lifecycle_key").unwrap();
let lifecycle_commits = store.get_commits(b"lifecycle_key").unwrap();
println!("Lifecycle key commits: {}", lifecycle_commits.len());
for (i, commit) in lifecycle_commits.iter().enumerate() {
println!(" {}: {} - {}", i, commit.id, commit.message);
}
assert_eq!(lifecycle_commits.len(), 5);
assert_eq!(lifecycle_commits[0].id, delete_commit); assert_eq!(lifecycle_commits[1].id, update3_commit); assert_eq!(lifecycle_commits[2].id, update2_commit); assert_eq!(lifecycle_commits[3].id, update1_commit); assert_eq!(lifecycle_commits[4].id, create_commit);
store
.insert(b"recreated_key".to_vec(), b"first_life".to_vec())
.unwrap();
let first_create = store.commit("First creation of recreated_key").unwrap();
store
.update(b"recreated_key".to_vec(), b"first_life_updated".to_vec())
.unwrap();
let first_update = store.commit("Update recreated_key in first life").unwrap();
store.delete(b"recreated_key").unwrap();
let first_delete = store.commit("Delete recreated_key").unwrap();
store
.insert(b"other_key".to_vec(), b"other_value".to_vec())
.unwrap();
let _other_commit = store.commit("Add some other key").unwrap();
store
.insert(b"recreated_key".to_vec(), b"second_life".to_vec())
.unwrap();
let second_create = store
.commit("Recreate recreated_key with new value")
.unwrap();
store
.update(b"recreated_key".to_vec(), b"second_life_updated".to_vec())
.unwrap();
let second_update = store.commit("Update recreated_key in second life").unwrap();
let recreated_commits = store.get_commits(b"recreated_key").unwrap();
println!("Recreated key commits: {}", recreated_commits.len());
for (i, commit) in recreated_commits.iter().enumerate() {
println!(" {}: {} - {}", i, commit.id, commit.message);
}
assert_eq!(recreated_commits.len(), 5);
assert_eq!(recreated_commits[0].id, second_update); assert_eq!(recreated_commits[1].id, second_create); assert_eq!(recreated_commits[2].id, first_delete); assert_eq!(recreated_commits[3].id, first_update); assert_eq!(recreated_commits[4].id, first_create);
store
.insert(b"static_key".to_vec(), b"never_changes".to_vec())
.unwrap();
let static_commit = store.commit("Add static key that never changes").unwrap();
store
.insert(b"dynamic_key".to_vec(), b"changes_a_lot".to_vec())
.unwrap();
store.commit("Add dynamic key").unwrap();
store
.update(b"dynamic_key".to_vec(), b"changed_once".to_vec())
.unwrap();
store.commit("Update dynamic key").unwrap();
store
.update(b"dynamic_key".to_vec(), b"changed_again".to_vec())
.unwrap();
store.commit("Update dynamic key again").unwrap();
let static_commits = store.get_commits(b"static_key").unwrap();
println!("Static key commits: {}", static_commits.len());
assert_eq!(static_commits.len(), 1);
assert_eq!(static_commits[0].id, static_commit);
println!("=== Key lifecycle patterns test completed successfully ===");
}
#[test]
fn test_get_commits_empty_and_edge_cases() {
let temp_dir = TempDir::new().unwrap();
gix::init(temp_dir.path()).unwrap();
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir_all(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
let nonexistent_commits = store.get_commits(b"does_not_exist").unwrap();
assert_eq!(
nonexistent_commits.len(),
0,
"Non-existent key should have no commits"
);
store
.insert(b"test_key".to_vec(), b"test_value".to_vec())
.unwrap();
let no_commits_yet = store.get_commits(b"test_key").unwrap();
assert_eq!(
no_commits_yet.len(),
0,
"Staged but uncommitted changes should show no commits"
);
let first_commit = store.commit("First commit ever").unwrap();
let after_first_commit = store.get_commits(b"test_key").unwrap();
assert_eq!(after_first_commit.len(), 1);
assert_eq!(after_first_commit[0].id, first_commit);
store.insert(b"empty_key".to_vec(), vec![]).unwrap();
let empty_value_commit = store.commit("Add key with empty value").unwrap();
let empty_key_commits = store.get_commits(b"empty_key").unwrap();
assert_eq!(empty_key_commits.len(), 1);
assert_eq!(empty_key_commits[0].id, empty_value_commit);
store
.insert(b"becomes_empty".to_vec(), b"has_content".to_vec())
.unwrap();
let content_commit = store.commit("Add key with content").unwrap();
store.update(b"becomes_empty".to_vec(), vec![]).unwrap();
let empty_update_commit = store.commit("Update key to empty value").unwrap();
let empty_update_commits = store.get_commits(b"becomes_empty").unwrap();
assert_eq!(empty_update_commits.len(), 2);
assert_eq!(empty_update_commits[0].id, empty_update_commit);
assert_eq!(empty_update_commits[1].id, content_commit);
let binary_key = vec![0x00, 0x01, 0x02, 0xFF, 0xFE];
let binary_value = vec![0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF];
store
.insert(binary_key.clone(), binary_value.clone())
.unwrap();
let binary_commit = store.commit("Add binary key-value pair").unwrap();
let binary_commits = store.get_commits(&binary_key).unwrap();
assert_eq!(binary_commits.len(), 1);
assert_eq!(binary_commits[0].id, binary_commit);
let long_key = b"very_long_key_name_".repeat(50); store
.insert(long_key.clone(), b"short_value".to_vec())
.unwrap();
let long_key_commit = store.commit("Add very long key name").unwrap();
let long_key_commits = store.get_commits(&long_key).unwrap();
assert_eq!(long_key_commits.len(), 1);
assert_eq!(long_key_commits[0].id, long_key_commit);
println!("=== Edge cases test completed successfully ===");
}
#[test]
fn test_thread_safe_basic_operations() {
let temp_dir = TempDir::new().unwrap();
std::process::Command::new("git")
.args(["init"])
.current_dir(&temp_dir)
.output()
.expect("Failed to initialize git repository");
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let store = ThreadSafeGitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store.insert(b"key1".to_vec(), b"value1".to_vec()).unwrap();
assert_eq!(store.get(b"key1"), Some(b"value1".to_vec()));
store.commit("Initial commit").unwrap();
store.update(b"key1".to_vec(), b"value2".to_vec()).unwrap();
assert_eq!(store.get(b"key1"), Some(b"value2".to_vec()));
}
#[test]
fn test_thread_safe_concurrent_access() {
use std::sync::Arc;
use std::thread;
let temp_dir = TempDir::new().unwrap();
std::process::Command::new("git")
.args(["init"])
.current_dir(&temp_dir)
.output()
.expect("Failed to initialize git repository");
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let store = Arc::new(ThreadSafeGitVersionedKvStore::<32>::init(&dataset_dir).unwrap());
let handles: Vec<_> = (0..5)
.map(|i| {
let store_clone = Arc::clone(&store);
thread::spawn(move || {
let key = format!("key{}", i).into_bytes();
let value = format!("value{}", i).into_bytes();
store_clone.insert(key.clone(), value.clone()).unwrap();
assert_eq!(store_clone.get(&key), Some(value));
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
store.commit("Concurrent insertions").unwrap();
let keys = store.list_keys().unwrap();
assert_eq!(keys.len(), 5);
}
#[test]
fn test_versioned_kv_store_merge() {
use crate::diff::TakeSourceResolver;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
std::process::Command::new("git")
.args(["init"])
.current_dir(&temp_dir)
.output()
.expect("Failed to initialize git repository");
std::process::Command::new("git")
.args(["config", "user.name", "Test User"])
.current_dir(&temp_dir)
.output()
.expect("Failed to configure git user name");
std::process::Command::new("git")
.args(["config", "user.email", "test@example.com"])
.current_dir(&temp_dir)
.output()
.expect("Failed to configure git user email");
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store
.insert(b"shared".to_vec(), b"original".to_vec())
.unwrap();
store
.insert(b"main_only".to_vec(), b"main_value".to_vec())
.unwrap();
store.commit("Initial commit on main").unwrap();
store.create_branch("feature").unwrap();
store
.insert(b"shared".to_vec(), b"feature_value".to_vec())
.unwrap();
store
.insert(b"feature_only".to_vec(), b"feature_value".to_vec())
.unwrap();
store.commit("Feature branch changes").unwrap();
store.checkout("main").unwrap();
store
.insert(b"shared".to_vec(), b"main_modified".to_vec())
.unwrap();
store
.insert(b"main_new".to_vec(), b"main_new_value".to_vec())
.unwrap();
store.commit("Main branch changes").unwrap();
let resolver = TakeSourceResolver;
let merge_result = store.merge("feature", &resolver);
assert!(
merge_result.is_ok(),
"Merge should succeed with conflict resolver"
);
let _merge_commit_id = merge_result.unwrap();
assert_eq!(
store.get(b"shared"),
Some(b"feature_value".to_vec()),
"shared value should be from feature branch"
);
assert_eq!(
store.get(b"main_only"),
Some(b"main_value".to_vec()),
"main_only should remain"
);
assert_eq!(
store.get(b"feature_only"),
Some(b"feature_value".to_vec()),
"feature_only should be added"
);
assert_eq!(
store.get(b"main_new"),
Some(b"main_new_value".to_vec()),
"main_new should remain"
);
assert_eq!(store.current_branch(), "main");
}
#[test]
fn test_versioned_kv_store_merge_ignore_conflicts() {
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
std::process::Command::new("git")
.args(["init"])
.current_dir(&temp_dir)
.output()
.expect("Failed to initialize git repository");
std::process::Command::new("git")
.args(["config", "user.name", "Test User"])
.current_dir(&temp_dir)
.output()
.expect("Failed to configure git user name");
std::process::Command::new("git")
.args(["config", "user.email", "test@example.com"])
.current_dir(&temp_dir)
.output()
.expect("Failed to configure git user email");
let dataset_dir = temp_dir.path().join("dataset");
std::fs::create_dir(&dataset_dir).unwrap();
let _cwd = CwdGuard::set(&dataset_dir);
let mut store = GitVersionedKvStore::<32>::init(&dataset_dir).unwrap();
store
.insert(b"key1".to_vec(), b"base_value".to_vec())
.unwrap();
store.commit("Base commit").unwrap();
store.create_branch("feature").unwrap();
store
.insert(b"key1".to_vec(), b"feature_value".to_vec())
.unwrap();
store
.insert(b"key2".to_vec(), b"feature_only".to_vec())
.unwrap();
store.commit("Feature changes").unwrap();
store.checkout("main").unwrap();
store
.insert(b"key1".to_vec(), b"main_value".to_vec())
.unwrap();
store.commit("Main changes").unwrap();
let merge_result = store.merge_ignore_conflicts("feature");
assert!(
merge_result.is_ok(),
"Merge ignore conflicts should succeed"
);
assert_eq!(store.get(b"key1"), Some(b"main_value".to_vec())); assert_eq!(store.get(b"key2"), Some(b"feature_only".to_vec())); }
}