use prikk_object::{ObjectEnvelope, ObjectType};
use crate::fsutil::{TestFailPoint, fail_after_for_test, fail_once_for_test};
use crate::test_support::{
dummy_signature, signed_empty_block_envelope, signed_patch_envelope, signed_ref_state_envelope,
signed_ref_update_envelope, unique_temp_dir,
};
use crate::worktree::materialize_manifest_entries;
use crate::{
ActiveLock, FileObjectStore, ObjectWriter, RefPublication, RefStore, RepoPath,
RepositoryLayout, SnapshotEntry, SnapshotManifest, Wal, write_active_ref_metadata,
};
#[test]
fn repository_format_create_sync_failure_retains_and_retries() -> prikk_error::Result<()> {
let root = unique_temp_dir("repository-sync-matrix");
fail_after_for_test(TestFailPoint::RequiredDirectorySync, 32);
assert!(RepositoryLayout::init(root.clone()).is_err());
assert!(
root.join(".prikk/FORMAT").is_file(),
"the create already landed before the directory sync failed"
);
assert!(RepositoryLayout::init(root.clone()).is_ok());
let _ = std::fs::remove_dir_all(root);
Ok(())
}
#[test]
fn object_write_sync_failure_retains_and_classifies() -> prikk_error::Result<()> {
for (skip, indexed_after_error) in [(0, false), (1, true)] {
let root = unique_temp_dir("object-sync-matrix");
let layout = RepositoryLayout::init(root.clone())?;
let mut object = ObjectEnvelope::unsigned(ObjectType::Blob, 1, b"sync".to_vec());
object.add_signature(dummy_signature())?;
let object_id = object.object_id();
let mut store = FileObjectStore::new(layout);
fail_after_for_test(TestFailPoint::RequiredFileSync, skip);
assert!(store.write_object(&object).is_err());
assert_eq!(
store.contains_object(ObjectType::Blob, object_id),
indexed_after_error
);
assert_eq!(store.write_object(&object)?, object_id);
assert!(store.contains_object(ObjectType::Blob, object_id));
let _ = std::fs::remove_dir_all(root);
}
Ok(())
}
#[test]
fn wal_parent_sync_failure_retains_one_record_and_retries() -> prikk_error::Result<()> {
let root = unique_temp_dir("wal-sync-matrix");
let layout = RepositoryLayout::init(root.clone())?;
let wal = Wal::for_layout(&layout);
let patch = signed_patch_envelope();
fail_once_for_test(TestFailPoint::RequiredDirectorySync);
assert!(wal.append_patch(&patch).is_err());
assert_eq!(wal.replay()?.records.len(), 1);
assert_eq!(wal.append_patch(&patch)?, 1);
assert_eq!(wal.replay()?.records.len(), 1);
let _ = std::fs::remove_dir_all(root);
Ok(())
}
#[test]
fn active_metadata_parent_sync_failure_retains_and_retries() -> prikk_error::Result<()> {
let root = unique_temp_dir("active-sync-matrix");
let layout = RepositoryLayout::init(root.clone())?;
fail_after_for_test(TestFailPoint::RequiredDirectorySync, 1);
assert!(write_active_ref_metadata(&layout, "heads/main").is_err());
assert_eq!(
std::fs::read(layout.default_active_ref_name_path())?,
b"heads/main"
);
assert!(write_active_ref_metadata(&layout, "heads/main").is_ok());
let _ = std::fs::remove_dir_all(root);
Ok(())
}
#[test]
fn ref_log_parent_sync_failure_retains_one_update_and_retries() -> prikk_error::Result<()> {
let root = unique_temp_dir("ref-sync-matrix");
let layout = RepositoryLayout::init(root.clone())?;
let mut objects = FileObjectStore::new(layout.clone());
let target = objects.write_object(&signed_empty_block_envelope())?;
let ref_state = signed_ref_state_envelope("heads/main", None, target, 1);
let ref_state_id = ref_state.object_id();
let publication = RefPublication {
ref_name: "heads/main".to_string(),
expected_previous_ref_state_id: None,
ref_update: signed_ref_update_envelope("heads/main", None, ref_state_id, target, 1),
ref_state,
};
let store = RefStore::new(layout);
fail_after_for_test(TestFailPoint::RequiredDirectorySync, 6);
assert!(store.publish(&publication).is_err());
assert_eq!(store.replay_log("heads/main")?.records.len(), 1);
assert_eq!(store.publish(&publication)?, ref_state_id);
assert_eq!(store.replay_log("heads/main")?.records.len(), 1);
let _ = std::fs::remove_dir_all(root);
Ok(())
}
#[test]
fn lock_parent_sync_failure_retains_stale_state_until_explicit_cleanup() -> prikk_error::Result<()>
{
let root = unique_temp_dir("lock-sync-matrix");
let layout = RepositoryLayout::init(root.clone())?;
let lock_path = layout.default_active_lock_path();
fail_once_for_test(TestFailPoint::RequiredDirectorySync);
assert!(ActiveLock::acquire(&layout).is_err());
assert!(lock_path.is_file());
assert!(ActiveLock::acquire(&layout).is_err());
std::fs::remove_file(&lock_path)?;
assert!(ActiveLock::acquire(&layout).is_ok());
let _ = std::fs::remove_dir_all(root);
Ok(())
}
#[test]
fn worktree_parent_sync_failure_is_repaired_before_unchanged_success() -> prikk_error::Result<()> {
let root = unique_temp_dir("worktree-sync-matrix");
let layout = RepositoryLayout::init(root.clone())?;
let manifest = SnapshotManifest {
files: vec![SnapshotEntry {
path: RepoPath::parse("file.txt")?,
bytes: b"content".to_vec(),
}],
};
fail_once_for_test(TestFailPoint::MutableParentSync);
assert!(materialize_manifest_entries(&layout, &manifest).is_err());
assert_eq!(std::fs::read(root.join("file.txt"))?, b"content");
fail_once_for_test(TestFailPoint::RequiredDirectorySync);
assert!(materialize_manifest_entries(&layout, &manifest).is_err());
assert!(materialize_manifest_entries(&layout, &manifest).is_ok());
let _ = std::fs::remove_dir_all(root);
Ok(())
}