use mkit_core::index::{self, IndexError};
use mkit_core::ops::restore::{self, RestoreError, RestoreOptions};
use mkit_core::{
Blob, EntryMode, MAX_TREE_DEPTH, Object, ObjectStore, StoreError, Tree, TreeEntry, diff_trees,
merge_trees, serialize,
};
use tempfile::TempDir;
type Hash = [u8; 32];
fn fresh() -> (TempDir, ObjectStore) {
let d = TempDir::new().unwrap();
let s = ObjectStore::init(d.path()).unwrap();
(d, s)
}
fn put_blob(s: &ObjectStore, data: &[u8]) -> Hash {
s.write(
&serialize::serialize(&Object::Blob(Blob {
data: data.to_vec(),
}))
.unwrap(),
)
.unwrap()
}
fn put_tree(s: &ObjectStore, entries: Vec<TreeEntry>) -> Hash {
s.write(&serialize::serialize(&Object::Tree(Tree { entries })).unwrap())
.unwrap()
}
fn deep_chain(s: &ObjectStore, levels: usize, leaf: &[u8]) -> Hash {
let leaf_blob = put_blob(s, leaf);
let mut cur = put_tree(
s,
vec![TreeEntry {
name: b"f".to_vec(),
mode: EntryMode::Blob,
object_hash: leaf_blob,
}],
);
for _ in 0..levels {
cur = put_tree(
s,
vec![TreeEntry {
name: b"d".to_vec(),
mode: EntryMode::Tree,
object_hash: cur,
}],
);
}
cur
}
fn over_cap() -> usize {
MAX_TREE_DEPTH + 8
}
#[test]
fn index_from_tree_rejects_deep_tree() {
let (_d, s) = fresh();
let root = deep_chain(&s, over_cap(), b"x");
let err = index::from_tree(&s, root).unwrap_err();
assert!(
matches!(err, IndexError::TreeTooDeep),
"expected IndexError::TreeTooDeep, got {err:?}"
);
}
#[test]
fn diff_rejects_deep_tree() {
let (_d, s) = fresh();
let old = deep_chain(&s, over_cap(), b"old");
let new = deep_chain(&s, over_cap(), b"new");
let err = diff_trees(&s, Some(old), Some(new)).unwrap_err();
assert!(
matches!(err, StoreError::TreeTooDeep),
"expected StoreError::TreeTooDeep, got {err:?}"
);
}
#[test]
fn diff_rejects_deep_added_tree() {
let (_d, s) = fresh();
let new = deep_chain(&s, over_cap(), b"x");
let err = diff_trees(&s, None, Some(new)).unwrap_err();
assert!(
matches!(err, StoreError::TreeTooDeep),
"expected StoreError::TreeTooDeep (added path), got {err:?}"
);
let err = diff_trees(&s, Some(new), None).unwrap_err();
assert!(
matches!(err, StoreError::TreeTooDeep),
"expected StoreError::TreeTooDeep (removed path), got {err:?}"
);
}
#[test]
fn merge_rejects_deep_tree() {
let (_d, s) = fresh();
let base = deep_chain(&s, over_cap(), b"base");
let ours = deep_chain(&s, over_cap(), b"ours");
let err = merge_trees(&s, Some(base), Some(ours), Some(base)).unwrap_err();
assert!(
matches!(err, StoreError::TreeTooDeep),
"expected StoreError::TreeTooDeep, got {err:?}"
);
}
#[test]
fn restore_rejects_deep_tree() {
let (_d, s) = fresh();
let root = deep_chain(&s, over_cap(), b"x");
let target = TempDir::new().unwrap();
let err =
restore::restore_tree_to_worktree(&s, &root, target.path(), &RestoreOptions::default())
.unwrap_err();
assert!(
matches!(err, RestoreError::TreeTooDeep),
"expected RestoreError::TreeTooDeep, got {err:?}"
);
}