#![cfg(test)]
use crate::git_ops::GitStore;
use crate::object::{canonical, GitHashKind, GitObjectKind};
use crate::pack_fixture::thin_ref_delta_pack;
use crate::store::tests::{one_blob_pack, tmpdir};
use git_storage_trait::GitOps;
fn body_of(i: usize) -> Vec<u8> {
format!("lane AI storm commit {i}\n").repeat(3).into_bytes()
}
fn storm(store: &GitStore, pushes: usize) -> (usize, Option<String>) {
let seed = b"lane AI seed\n";
let (p0, oid0) = one_blob_pack(seed);
store.put_pack(&p0).expect("the seed pack");
store.wait_indexed();
let mut base_oid = oid0;
let mut base_len = seed.len() as u64;
let mut fails = 0usize;
let mut first: Option<String> = None;
for i in 0..pushes {
let body = body_of(i);
let pack = thin_ref_delta_pack(&base_oid, base_len, &body);
let r = store
.put_pack(&pack)
.and_then(|_| store.index_now().map(|_| ()));
if let Err(e) = r {
fails += 1;
if first.is_none() {
first = Some(format!("push {i}: {e:#}"));
}
}
base_oid = GitHashKind::Sha1.oid_of(&canonical(GitObjectKind::Blob, &body));
base_len = body.len() as u64;
}
(fails, first)
}
#[test]
fn a_hundred_and_fifty_single_commit_pushes_are_all_accepted() {
let dir = tmpdir("laneAI-storm-ack");
let store = GitStore::open(&dir, "alice").unwrap();
let (fails, first) = storm(&store, 150);
assert_eq!(
fails,
0,
"{} of 150 single-commit pushes were refused; the first was {}",
fails,
first.unwrap_or_default()
);
assert!(
store.unindexable_packs().is_empty(),
"the store gave up on {:?}",
store.unindexable_packs()
);
}
#[test]
fn every_object_of_a_long_chain_reads_back_byte_correct() {
let dir = tmpdir("laneAI-storm-read");
let store = GitStore::open(&dir, "alice").unwrap();
const N: usize = 150;
let (fails, first) = storm(&store, N);
assert_eq!(fails, 0, "{}", first.unwrap_or_default());
store.wait_indexed();
let hash = store.hash_kind();
let mut checked = 0usize;
for i in 0..N {
let body = body_of(i);
let oid = hash.oid_of(&canonical(GitObjectKind::Blob, &body));
let (kind, payload) = store
.content(&oid)
.unwrap_or_else(|e| panic!("reading the blob of push {i} (chain depth {i}): {e:#}"))
.unwrap_or_else(|| panic!("the blob of push {i} is absent after its push was acked"));
assert_eq!(kind, GitObjectKind::Blob);
assert_eq!(payload, body, "the blob of push {i} came back wrong");
checked += 1;
}
assert_eq!(checked, N);
}
#[test]
fn a_deep_chain_resolves_and_a_real_cycle_is_refused_by_name() {
use crate::resolve::{materialize_at, ArchiveBytes, NoBases};
struct SelfReferential(Vec<u8>);
impl ArchiveBytes for SelfReferential {
fn at(&self, at: u64, len: u64) -> anyhow::Result<std::borrow::Cow<'_, [u8]>> {
let at = at as usize;
if at >= self.0.len() {
return Ok(std::borrow::Cow::Owned(Vec::new()));
}
let end = (at + len as usize).min(self.0.len());
Ok(std::borrow::Cow::Borrowed(&self.0[at..end]))
}
}
let oid = vec![7u8; 20];
let entry = {
let delta = crate::pack_fixture::insert_only_delta(4, b"abcd");
let mut e = Vec::new();
crate::pack_walk::encode_type_and_size(
&mut e,
crate::index_layout::ObjType::RefDelta,
delta.len() as u64,
);
e.extend_from_slice(&oid);
e.extend_from_slice(&crate::pack_fixture::deflate(&delta));
e
};
let err = materialize_at(
&SelfReferential(entry),
0,
20,
&|_| Some(0),
&NoBases,
)
.expect_err("a self-referential ref-delta must not resolve");
let text = format!("{err:#}");
assert!(
text.contains("form a cycle") && text.contains("returns to archive offset 0"),
"a cycle must be refused by name and by offset, not by a link count: {text}"
);
let dir = tmpdir("laneAI-deep-chain");
let store = GitStore::open(&dir, "alice").unwrap();
let (fails, first) = storm(&store, 300);
assert_eq!(
fails,
0,
"a 300-link chain is deep, not corrupt: {}",
first.unwrap_or_default()
);
}
#[test]
fn a_base_source_that_errors_says_so_instead_of_saying_absent() {
use crate::resolve::{resolve, BaseSource};
struct Broken;
impl BaseSource for Broken {
fn content(&self, _oid: &[u8]) -> anyhow::Result<Option<(GitObjectKind, Vec<u8>)>> {
anyhow::bail!("the store holds this base and could not read it back")
}
}
let base_oid = vec![9u8; 20];
let pack = crate::pack_fixture::thin_ref_delta_pack(&base_oid, 4, b"abcd");
let err = resolve(&pack, GitHashKind::Sha1, 0, &Broken)
.expect_err("a thin pack whose base source fails must not resolve");
let text = format!("{err:#}");
assert!(
text.contains("could not read it back"),
"the base source's own sentence must survive to the caller: {text}"
);
assert!(
!text.contains("genuinely does not have that object"),
"a source that FAILED was reported as a repository that does not HOLD the object: {text}"
);
}