From 4a7ef64c776faf3291183e02e3d09ed3525e3db5 Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:03 +0700 Subject: [PATCH 01/12] test(core): remove inline tests from binary_decoder --- libvctrl_core/src/codec/binary_decoder.rs | 528 ---------------------- 1 file changed, 528 deletions(-) diff --git a/libvctrl_core/src/codec/binary_decoder.rs b/libvctrl_core/src/codec/binary_decoder.rs index 704bc03..e8a07d8 100644 --- a/libvctrl_core/src/codec/binary_decoder.rs +++ b/libvctrl_core/src/codec/binary_decoder.rs @@ -392,531 +392,3 @@ impl Decoder for BinaryDecoder { Tag::with_meta(name, target, tagger, message, meta) } } - -#[cfg(test)] -mod tests { - use super::*; - use std::io::Cursor; - - fn hash_bytes(fill: u8) -> Vec { - vec![fill; HASH_LENGTH] - } - - // --- Private helper tests --- - - #[test] - fn test_check_version_missing_byte() { - let result = BinaryDecoder::check_version(&[]); - assert!(result.is_err(), "empty data should fail"); - } - - #[test] - fn test_check_version_wrong_version() { - let result = BinaryDecoder::check_version(&[0u8, 0xAA]); - assert!(result.is_err(), "wrong version should fail"); - } - - #[test] - fn test_check_version_no_payload() { - let result = BinaryDecoder::check_version(&[EXPECTED_VERSION]); - assert!( - result.is_err(), - "version byte only (no payload) should fail" - ); - } - - #[test] - fn test_check_version_valid() { - let data = [EXPECTED_VERSION, 0xAA, 0xBB, 0xCC]; - let result = BinaryDecoder::check_version(&data); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), &[0xAA, 0xBB, 0xCC]); - } - - #[test] - fn test_read_bounded_within_limit() { - let data = vec![0x42u8; 50]; - let mut cursor = Cursor::new(data.as_slice()); - let result = BinaryDecoder::read_bounded(&mut cursor, 100); - assert!(result.is_ok()); - let buf = result.unwrap(); - assert_eq!(buf.len(), 50); - assert!(buf.iter().all(|&b| b == 0x42)); - } - - #[test] - fn test_read_bounded_exceeds_limit() { - let data = vec![0u8; 100]; - let mut cursor = Cursor::new(data.as_slice()); - let result = BinaryDecoder::read_bounded(&mut cursor, 50); - assert!(result.is_err(), "should error when stream exceeds max size"); - } - - #[test] - fn test_read_bounded_empty_stream() { - let data: Vec = Vec::new(); - let mut cursor = Cursor::new(data.as_slice()); - let result = BinaryDecoder::read_bounded(&mut cursor, 100); - assert!(result.is_ok()); - assert!(result.unwrap().is_empty()); - } - - #[test] - fn test_require_byte_valid() { - let data = [10, 20, 30]; - let result = BinaryDecoder::require_byte(&data, 1, "test byte"); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), 20); - } - - #[test] - fn test_require_byte_out_of_bounds() { - let data = [10]; - let result = BinaryDecoder::require_byte(&data, 5, "test byte"); - assert!(result.is_err()); - } - - #[test] - fn test_require_slice_valid() { - let data = [1, 2, 3, 4, 5]; - let result = BinaryDecoder::require_slice(&data, 1, 3, "test slice"); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), &[2, 3, 4]); - } - - #[test] - fn test_require_slice_zero_length() { - let data = [1, 2, 3]; - let result = BinaryDecoder::require_slice(&data, 0, 0, "empty"); - assert!(result.is_ok()); - assert!(result.unwrap().is_empty()); - } - - #[test] - fn test_require_slice_truncated() { - let data = [1, 2]; - let result = BinaryDecoder::require_slice(&data, 0, 5, "test slice"); - assert!(result.is_err()); - } - - #[test] - fn test_require_slice_overflow() { - let data = [1, 2]; - let result = BinaryDecoder::require_slice(&data, usize::MAX, 2, "overflow slice"); - assert!( - result.is_err(), - "should error on usize overflow in start+len" - ); - } - - // --- decode_blob tests --- - - #[test] - fn test_decode_blob_valid() { - let payload = b"hello world"; - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&(payload.len() as u64).to_le_bytes()); - data.extend_from_slice(payload); - - let result = BinaryDecoder.decode_blob(Cursor::new(data)); - assert!(result.is_ok()); - assert_eq!(result.unwrap().data(), payload.as_slice()); - } - - #[test] - fn test_decode_blob_empty_payload() { - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&0u64.to_le_bytes()); - - let result = BinaryDecoder.decode_blob(Cursor::new(data)); - assert!(result.is_ok()); - assert!(result.unwrap().data().is_empty()); - } - - #[test] - fn test_decode_blob_empty_input() { - let result = BinaryDecoder.decode_blob(Cursor::new(Vec::::new())); - assert!(result.is_err()); - } - - #[test] - fn test_decode_blob_wrong_version() { - let mut data = Vec::new(); - data.push(0); - data.extend_from_slice(&5u64.to_le_bytes()); - data.extend_from_slice(b"hello"); - - let result = BinaryDecoder.decode_blob(Cursor::new(data)); - assert!(result.is_err()); - } - - #[test] - fn test_decode_blob_length_mismatch_too_short() { - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&100u64.to_le_bytes()); - data.extend_from_slice(b"short"); - - let result = BinaryDecoder.decode_blob(Cursor::new(data)); - assert!(result.is_err()); - } - - #[test] - fn test_decode_blob_length_mismatch_too_long() { - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&2u64.to_le_bytes()); - data.extend_from_slice(b"this is longer than 2"); - - let result = BinaryDecoder.decode_blob(Cursor::new(data)); - assert!(result.is_err()); - } - - // --- decode_tree tests --- - - #[test] - fn test_decode_tree_valid_single_entry() { - let hb = hash_bytes(0xAB); - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&1u32.to_le_bytes()); - data.push(4); - data.extend_from_slice(b"file"); - data.push(0); // Blob - data.extend_from_slice(&hb); - - let result = BinaryDecoder.decode_tree(Cursor::new(data)); - assert!(result.is_ok()); - let tree = result.unwrap(); - assert_eq!(tree.entries().len(), 1); - assert_eq!(tree.entries()[0].name(), "file"); - assert_eq!(tree.entries()[0].kind(), EntryKind::Blob); - } - - #[test] - fn test_decode_tree_empty() { - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&0u32.to_le_bytes()); - - let result = BinaryDecoder.decode_tree(Cursor::new(data)); - assert!(result.is_ok()); - assert_eq!(result.unwrap().entries().len(), 0); - } - - #[test] - fn test_decode_tree_multiple_entries() { - let hb1 = hash_bytes(0x01); - let hb2 = hash_bytes(0x02); - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&2u32.to_le_bytes()); - // Entry 1 - data.push(3); - data.extend_from_slice(b"src"); - data.push(3); // Tree - data.extend_from_slice(&hb1); - // Entry 2 - data.push(9); - data.extend_from_slice(b"Cargo.toml"); - data.push(0); // Blob - data.extend_from_slice(&hb2); - - let result = BinaryDecoder.decode_tree(Cursor::new(data)); - assert!(result.is_ok()); - let tree = result.unwrap(); - assert_eq!(tree.entries().len(), 2); - assert_eq!(tree.entries()[0].name(), "src"); - assert_eq!(tree.entries()[0].kind(), EntryKind::Tree); - assert_eq!(tree.entries()[1].name(), "Cargo.toml"); - assert_eq!(tree.entries()[1].kind(), EntryKind::Blob); - } - - #[test] - fn test_decode_tree_unknown_kind() { - let hb = hash_bytes(0x00); - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&1u32.to_le_bytes()); - data.push(1); - data.push(b'x'); - data.push(99); // unknown kind - data.extend_from_slice(&hb); - - let result = BinaryDecoder.decode_tree(Cursor::new(data)); - assert!(result.is_err()); - } - - #[test] - fn test_decode_tree_trailing_bytes() { - let hb = hash_bytes(0x00); - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&1u32.to_le_bytes()); - data.push(1); - data.push(b'x'); - data.push(0); - data.extend_from_slice(&hb); - data.push(0xFF); // trailing - - let result = BinaryDecoder.decode_tree(Cursor::new(data)); - assert!(result.is_err()); - } - - #[test] - fn test_decode_tree_all_known_kinds() { - let kinds = [0u8, 1, 2, 3, 4]; // Blob, Executable, Symlink, Tree, Submodule - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&(kinds.len() as u32).to_le_bytes()); - for (i, &kind) in kinds.iter().enumerate() { - let name = format!("entry_{i}"); - data.push(name.len() as u8); - data.extend_from_slice(name.as_bytes()); - data.push(kind); - data.extend_from_slice(&hash_bytes(i as u8)); - } - - let result = BinaryDecoder.decode_tree(Cursor::new(data)); - assert!(result.is_ok(), "should decode all known entry kinds"); - } - - // --- decode_commit tests --- - - fn build_valid_commit_bytes( - tree_fill: u8, - parents: &[u8], - author_name: &str, - author_email: &str, - committer_name: &str, - committer_email: &str, - message: &str, - timestamp: i64, - tz: i16, - encoding: Option<&str>, - ) -> Vec { - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.extend_from_slice(&hash_bytes(tree_fill)); - data.extend_from_slice(&(parents.len() as u16).to_le_bytes()); - for &p in parents { - data.extend_from_slice(&hash_bytes(p)); - } - data.push(author_name.len() as u8); - data.extend_from_slice(author_name.as_bytes()); - data.push(author_email.len() as u8); - data.extend_from_slice(author_email.as_bytes()); - data.push(committer_name.len() as u8); - data.extend_from_slice(committer_name.as_bytes()); - data.push(committer_email.len() as u8); - data.extend_from_slice(committer_email.as_bytes()); - data.extend_from_slice(&(message.len() as u32).to_le_bytes()); - data.extend_from_slice(message.as_bytes()); - data.extend_from_slice(×tamp.to_le_bytes()); - data.extend_from_slice(&tz.to_le_bytes()); - match encoding { - Some(enc) => { - data.push(enc.len() as u8); - data.extend_from_slice(enc.as_bytes()); - } - None => data.push(0), - } - data - } - - #[test] - fn test_decode_commit_valid_no_parents() { - let data = build_valid_commit_bytes( - 0x01, - &[], - "Alice", - "a@b.c", - "Bob", - "b@c.d", - "init", - 1700000000, - 0, - None, - ); - let result = BinaryDecoder.decode_commit(Cursor::new(data)); - assert!(result.is_ok()); - let commit = result.unwrap(); - assert_eq!(commit.parents().len(), 0); - assert_eq!(commit.author().name(), "Alice"); - assert_eq!(commit.author().email(), "a@b.c"); - assert_eq!(commit.committer().name(), "Bob"); - assert_eq!(commit.committer().email(), "b@c.d"); - assert_eq!(commit.message(), "init"); - assert_eq!(commit.meta().timestamp(), 1700000000); - assert_eq!(commit.meta().timezone_offset(), 0); - assert!(commit.meta().encoding().is_none()); - } - - #[test] - fn test_decode_commit_with_parents_and_encoding() { - let data = build_valid_commit_bytes( - 0x01, - &[0x02, 0x03], - "Alice", - "alice@ex.com", - "Bob", - "bob@ex.com", - "merge", - 1700000000, - 3600, - Some("UTF-8"), - ); - let result = BinaryDecoder.decode_commit(Cursor::new(data)); - assert!(result.is_ok()); - let commit = result.unwrap(); - assert_eq!(commit.parents().len(), 2); - assert_eq!(commit.meta().timezone_offset(), 3600); - assert_eq!(commit.meta().encoding(), Some("UTF-8")); - assert_eq!(commit.message(), "merge"); - } - - #[test] - fn test_decode_commit_trailing_bytes() { - let mut data = - build_valid_commit_bytes(0x01, &[], "A", "a@b.c", "B", "b@c.d", "m", 0, 0, None); - data.push(0xFF); - let result = BinaryDecoder.decode_commit(Cursor::new(data)); - assert!(result.is_err()); - } - - #[test] - fn test_decode_commit_wrong_version() { - let mut data = - build_valid_commit_bytes(0x01, &[], "A", "a@b.c", "B", "b@c.d", "m", 0, 0, None); - data[0] = 0; - let result = BinaryDecoder.decode_commit(Cursor::new(data)); - assert!(result.is_err()); - } - - #[test] - fn test_decode_commit_empty_message() { - let data = - build_valid_commit_bytes(0x01, &[], "A", "a@b.c", "B", "b@c.d", "", 100, 0, None); - let result = BinaryDecoder.decode_commit(Cursor::new(data)); - assert!(result.is_ok()); - assert_eq!(result.unwrap().message(), ""); - } - - // --- decode_tag tests --- - - fn build_valid_tag_bytes( - name: &str, - target_fill: u8, - tagger: Option<(&str, &str)>, - message: &str, - timestamp: i64, - tz: i16, - encoding: Option<&str>, - ) -> Vec { - let mut data = Vec::new(); - data.push(EXPECTED_VERSION); - data.push(name.len() as u8); - data.extend_from_slice(name.as_bytes()); - data.extend_from_slice(&hash_bytes(target_fill)); - match tagger { - Some((tname, temail)) => { - data.push(1); - data.push(tname.len() as u8); - data.extend_from_slice(tname.as_bytes()); - data.push(temail.len() as u8); - data.extend_from_slice(temail.as_bytes()); - } - None => data.push(0), - } - data.extend_from_slice(&(message.len() as u32).to_le_bytes()); - data.extend_from_slice(message.as_bytes()); - data.extend_from_slice(×tamp.to_le_bytes()); - data.extend_from_slice(&tz.to_le_bytes()); - match encoding { - Some(enc) => { - data.push(enc.len() as u8); - data.extend_from_slice(enc.as_bytes()); - } - None => data.push(0), - } - data - } - - #[test] - fn test_decode_tag_valid_with_tagger() { - let data = build_valid_tag_bytes( - "v1.0", - 0x10, - Some(("Alice", "alice@ex.com")), - "release", - 1700000000, - 0, - None, - ); - let result = BinaryDecoder.decode_tag(Cursor::new(data)); - assert!(result.is_ok()); - let tag = result.unwrap(); - assert_eq!(tag.name(), "v1.0"); - assert!(tag.tagger().is_some()); - assert_eq!(tag.tagger().unwrap().name(), "Alice"); - assert_eq!(tag.tagger().unwrap().email(), "alice@ex.com"); - assert_eq!(tag.message(), "release"); - } - - #[test] - fn test_decode_tag_no_tagger() { - let data = build_valid_tag_bytes("v2.0", 0x20, None, "", 1700000000, 0, None); - let result = BinaryDecoder.decode_tag(Cursor::new(data)); - assert!(result.is_ok()); - let tag = result.unwrap(); - assert_eq!(tag.name(), "v2.0"); - assert!(tag.tagger().is_none()); - assert_eq!(tag.message(), ""); - } - - #[test] - fn test_decode_tag_with_encoding() { - let data = build_valid_tag_bytes( - "v3.0", - 0x30, - Some(("Bob", "bob@ex.com")), - "annotated", - 1700000000, - -3600, - Some("UTF-8"), - ); - let result = BinaryDecoder.decode_tag(Cursor::new(data)); - assert!(result.is_ok()); - let tag = result.unwrap(); - assert_eq!(tag.meta().timezone_offset(), -3600); - assert_eq!(tag.meta().encoding(), Some("UTF-8")); - } - - #[test] - fn test_decode_tag_invalid_tagger_presence() { - let data = build_valid_tag_bytes("v4.0", 0x40, None, "", 0, 0, None); - let pos = 1 + 4 + HASH_LENGTH; // after name + target - let mut mutable_data = data; - mutable_data[pos] = 5; // invalid tagger presence byte - let result = BinaryDecoder.decode_tag(Cursor::new(mutable_data)); - assert!(result.is_err()); - } - - #[test] - fn test_decode_tag_trailing_bytes() { - let mut data = build_valid_tag_bytes("v5.0", 0x50, None, "", 0, 0, None); - data.push(0xFF); - let result = BinaryDecoder.decode_tag(Cursor::new(data)); - assert!(result.is_err()); - } - - #[test] - fn test_decode_tag_wrong_version() { - let mut data = build_valid_tag_bytes("v6.0", 0x60, None, "", 0, 0, None); - data[0] = 99; - let result = BinaryDecoder.decode_tag(Cursor::new(data)); - assert!(result.is_err()); - } -} From b098e032099e751f14bb4c6c0d6d81b911154bac Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:04 +0700 Subject: [PATCH 02/12] test(core): remove inline tests from binary_encoder --- libvctrl_core/src/codec/binary_encoder.rs | 305 ---------------------- 1 file changed, 305 deletions(-) diff --git a/libvctrl_core/src/codec/binary_encoder.rs b/libvctrl_core/src/codec/binary_encoder.rs index 6bf6721..a7ba6bc 100644 --- a/libvctrl_core/src/codec/binary_encoder.rs +++ b/libvctrl_core/src/codec/binary_encoder.rs @@ -237,308 +237,3 @@ impl Encoder for BinaryEncoder { Ok(()) } } - -#[cfg(test)] -mod tests { - use super::*; - use libvctrl_handler::{CommitMeta, HASH_LENGTH, Hash, UserID}; - use std::io::Cursor; - - fn make_hash(fill: u8) -> Hash { - Hash::from_bytes(&vec![fill; HASH_LENGTH]).unwrap() - } - - fn hash_bytes(fill: u8) -> Vec { - vec![fill; HASH_LENGTH] - } - - fn make_user_id(name: &str, email: &str) -> UserID { - UserID::new(name.into(), email.into()).unwrap() - } - - fn make_meta(ts: i64, tz: i16, enc: Option<&str>) -> CommitMeta { - CommitMeta::new(ts, tz, enc.map(|s| s.into())).unwrap() - } - - #[test] - fn test_encode_blob() { - let blob = Blob::new(vec![0x01, 0x02, 0x03]).unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_blob(&blob, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.extend_from_slice(&3u64.to_le_bytes()); - expected.extend_from_slice(&[0x01, 0x02, 0x03]); - assert_eq!(encoded, expected); - } - - #[test] - fn test_encode_blob_empty_data() { - let blob = Blob::new(vec![]).unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_blob(&blob, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.extend_from_slice(&0u64.to_le_bytes()); - assert_eq!(encoded, expected); - } - - #[test] - fn test_encode_tree_single_entry() { - let hash = make_hash(0xAB); - let entry = TreeEntry::new("README".into(), EntryKind::Blob, hash).unwrap(); - let tree = Tree::new(vec![entry]).unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_tree(&tree, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.extend_from_slice(&1u32.to_le_bytes()); - expected.push(6); // "README" length - expected.extend_from_slice(b"README"); - expected.push(0); // Blob - expected.extend_from_slice(&hash_bytes(0xAB)); - assert_eq!(encoded, expected); - } - - #[test] - fn test_encode_tree_empty() { - let tree = Tree::new(vec![]).unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_tree(&tree, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.extend_from_slice(&0u32.to_le_bytes()); - assert_eq!(encoded, expected); - } - - #[test] - fn test_encode_tree_multiple_entries() { - let e1 = TreeEntry::new("src".into(), EntryKind::Tree, make_hash(0x01)).unwrap(); - let e2 = TreeEntry::new("run".into(), EntryKind::Executable, make_hash(0x02)).unwrap(); - let tree = Tree::new(vec![e1, e2]).unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_tree(&tree, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.extend_from_slice(&2u32.to_le_bytes()); - expected.push(3); - expected.extend_from_slice(b"src"); - expected.push(3); // Tree - expected.extend_from_slice(&hash_bytes(0x01)); - expected.push(3); - expected.extend_from_slice(b"run"); - expected.push(1); // Executable - expected.extend_from_slice(&hash_bytes(0x02)); - assert_eq!(encoded, expected); - } - - #[test] - fn test_encode_commit() { - let commit = Commit::with_meta( - make_hash(0x01), - vec![], - make_user_id("Alice", "a@b.c"), - make_user_id("Bob", "b@c.d"), - "init".into(), - make_meta(1700000000, 0, None), - ) - .unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_commit(&commit, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.extend_from_slice(&hash_bytes(0x01)); - expected.extend_from_slice(&0u16.to_le_bytes()); - expected.push(5); - expected.extend_from_slice(b"Alice"); - expected.push(5); - expected.extend_from_slice(b"a@b.c"); - expected.push(3); - expected.extend_from_slice(b"Bob"); - expected.push(5); - expected.extend_from_slice(b"b@c.d"); - expected.extend_from_slice(&4u32.to_le_bytes()); - expected.extend_from_slice(b"init"); - expected.extend_from_slice(&1700000000i64.to_le_bytes()); - expected.extend_from_slice(&0i16.to_le_bytes()); - expected.push(0); - assert_eq!(encoded, expected); - } - - #[test] - fn test_encode_commit_with_encoding() { - let commit = Commit::with_meta( - make_hash(0x01), - vec![], - make_user_id("A", "a@b.c"), - make_user_id("B", "b@c.d"), - "msg".into(), - make_meta(1700000000, 3600, Some("UTF-8")), - ) - .unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_commit(&commit, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.extend_from_slice(&hash_bytes(0x01)); - expected.extend_from_slice(&0u16.to_le_bytes()); - expected.push(1); - expected.extend_from_slice(b"A"); - expected.push(5); - expected.extend_from_slice(b"a@b.c"); - expected.push(1); - expected.extend_from_slice(b"B"); - expected.push(5); - expected.extend_from_slice(b"b@c.d"); - expected.extend_from_slice(&3u32.to_le_bytes()); - expected.extend_from_slice(b"msg"); - expected.extend_from_slice(&1700000000i64.to_le_bytes()); - expected.extend_from_slice(&3600i16.to_le_bytes()); - expected.push(5); - expected.extend_from_slice(b"UTF-8"); - assert_eq!(encoded, expected); - } - - #[test] - fn test_encode_commit_with_parents() { - let commit = Commit::with_meta( - make_hash(0x01), - vec![make_hash(0x02), make_hash(0x03)], - make_user_id("A", "a@b.c"), - make_user_id("B", "b@c.d"), - "merge".into(), - make_meta(0, 0, None), - ) - .unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_commit(&commit, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.extend_from_slice(&hash_bytes(0x01)); - expected.extend_from_slice(&2u16.to_le_bytes()); - expected.extend_from_slice(&hash_bytes(0x02)); - expected.extend_from_slice(&hash_bytes(0x03)); - expected.push(1); - expected.extend_from_slice(b"A"); - expected.push(5); - expected.extend_from_slice(b"a@b.c"); - expected.push(1); - expected.extend_from_slice(b"B"); - expected.push(5); - expected.extend_from_slice(b"b@c.d"); - expected.extend_from_slice(&5u32.to_le_bytes()); - expected.extend_from_slice(b"merge"); - expected.extend_from_slice(&0i64.to_le_bytes()); - expected.extend_from_slice(&0i16.to_le_bytes()); - expected.push(0); - assert_eq!(encoded, expected); - } - - #[test] - fn test_encode_tag_with_tagger() { - let tag = Tag::with_meta( - "v1.0".into(), - make_hash(0x10), - Some(make_user_id("Alice", "alice@ex.com")), - "release".into(), - make_meta(1700000000, 0, None), - ) - .unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_tag(&tag, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.push(4); // "v1.0" length - expected.extend_from_slice(b"v1.0"); - expected.extend_from_slice(&hash_bytes(0x10)); - expected.push(1); // has tagger - expected.push(5); - expected.extend_from_slice(b"Alice"); - expected.push(11); - expected.extend_from_slice(b"alice@ex.com"); - expected.extend_from_slice(&7u32.to_le_bytes()); - expected.extend_from_slice(b"release"); - expected.extend_from_slice(&1700000000i64.to_le_bytes()); - expected.extend_from_slice(&0i16.to_le_bytes()); - expected.push(0); - assert_eq!(encoded, expected); - } - - #[test] - fn test_encode_tag_no_tagger() { - let tag = Tag::with_meta( - "v2.0".into(), - make_hash(0x20), - None, - "".into(), - make_meta(1700000000, 0, None), - ) - .unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_tag(&tag, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.push(4); - expected.extend_from_slice(b"v2.0"); - expected.extend_from_slice(&hash_bytes(0x20)); - expected.push(0); // no tagger - expected.extend_from_slice(&0u32.to_le_bytes()); - expected.extend_from_slice(&1700000000i64.to_le_bytes()); - expected.extend_from_slice(&0i16.to_le_bytes()); - expected.push(0); - assert_eq!(encoded, expected); - } - - #[test] - fn test_encode_tag_with_encoding() { - let tag = Tag::with_meta( - "v3".into(), - make_hash(0x30), - Some(make_user_id("B", "b@c.d")), - "tag".into(), - make_meta(1700000000, -3600, Some("UTF-8")), - ) - .unwrap(); - let mut buf = Cursor::new(Vec::new()); - BinaryEncoder.encode_tag(&tag, &mut buf).unwrap(); - let encoded = buf.into_inner(); - - let mut expected = Vec::new(); - expected.push(VERSION); - expected.push(2); // "v3" length - expected.extend_from_slice(b"v3"); - expected.extend_from_slice(&hash_bytes(0x30)); - expected.push(1); - expected.push(1); - expected.extend_from_slice(b"B"); - expected.push(5); - expected.extend_from_slice(b"b@c.d"); - expected.extend_from_slice(&3u32.to_le_bytes()); - expected.extend_from_slice(b"tag"); - expected.extend_from_slice(&1700000000i64.to_le_bytes()); - expected.extend_from_slice(&(-3600i16).to_le_bytes()); - expected.push(5); - expected.extend_from_slice(b"UTF-8"); - assert_eq!(encoded, expected); - } -} From 3cc0491ebf2fc96dfa1db756b35d327ef6b90df4 Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:04 +0700 Subject: [PATCH 03/12] test(core): remove inline tests from sha512 --- libvctrl_core/src/hash/sha512.rs | 74 -------------------------------- 1 file changed, 74 deletions(-) diff --git a/libvctrl_core/src/hash/sha512.rs b/libvctrl_core/src/hash/sha512.rs index 321b555..c5f3e37 100644 --- a/libvctrl_core/src/hash/sha512.rs +++ b/libvctrl_core/src/hash/sha512.rs @@ -28,77 +28,3 @@ impl Hasher for Sha512Hasher { Hash::from_bytes(&digest) } } - -#[cfg(test)] -mod tests { - use super::*; - use libvctrl_handler::HASH_LENGTH; - use std::io::Cursor; - - #[test] - fn test_hash_empty_input() { - let cursor = Cursor::new(Vec::::new()); - let result = Sha512Hasher.hash(cursor); - assert!(result.is_ok(), "hashing empty input should succeed"); - let hash = result.unwrap(); - assert_eq!( - hash.as_bytes().len(), - HASH_LENGTH, - "hash should be HASH_LENGTH bytes" - ); - } - - #[test] - fn test_hash_non_empty_input() { - let cursor = Cursor::new(b"hello world"); - let result = Sha512Hasher.hash(cursor); - assert!(result.is_ok()); - let hash = result.unwrap(); - assert_eq!(hash.as_bytes().len(), HASH_LENGTH); - } - - #[test] - fn test_hash_deterministic() { - let data = b"test data for determinism check"; - let h1 = Sha512Hasher.hash(Cursor::new(data.as_slice())).unwrap(); - let h2 = Sha512Hasher.hash(Cursor::new(data.as_slice())).unwrap(); - assert_eq!( - h1.as_bytes(), - h2.as_bytes(), - "same input must produce identical hash" - ); - } - - #[test] - fn test_hash_different_inputs_produce_different_hashes() { - let h1 = Sha512Hasher.hash(Cursor::new(b"input one")).unwrap(); - let h2 = Sha512Hasher.hash(Cursor::new(b"input two")).unwrap(); - assert_ne!( - h1.as_bytes(), - h2.as_bytes(), - "different inputs should produce different hashes" - ); - } - - #[test] - fn test_hash_large_input() { - let data = vec![0xABu8; 100_000]; - let result = Sha512Hasher.hash(Cursor::new(data)); - assert!(result.is_ok(), "hashing large input should succeed"); - let hash = result.unwrap(); - assert_eq!(hash.as_bytes().len(), HASH_LENGTH); - } - - #[test] - fn test_hash_single_byte() { - let result = Sha512Hasher.hash(Cursor::new(b"\x00")); - assert!(result.is_ok()); - let result2 = Sha512Hasher.hash(Cursor::new(b"\xFF")); - assert!(result2.is_ok()); - assert_ne!( - result.unwrap().as_bytes(), - result2.unwrap().as_bytes(), - "different single bytes should produce different hashes" - ); - } -} From 15ce08fc02b14a7d1519bbd887f0237e63828e8a Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:04 +0700 Subject: [PATCH 04/12] test(core): remove inline tests from blob --- libvctrl_core/src/object/blob.rs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/libvctrl_core/src/object/blob.rs b/libvctrl_core/src/object/blob.rs index 9e8fdea..ef22996 100644 --- a/libvctrl_core/src/object/blob.rs +++ b/libvctrl_core/src/object/blob.rs @@ -21,24 +21,3 @@ impl BlobBuilder { Blob::new(self.data) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_build_success_with_data() { - let result = BlobBuilder::new().with_data(vec![1, 2, 3, 4]).build(); - assert!(result.is_ok(), "BlobBuilder should succeed with valid data"); - } - - #[test] - fn test_build_returns_blob_with_correct_data() { - let data = vec![0xDE, 0xAD, 0xBE, 0xEF]; - let blob = BlobBuilder::new() - .with_data(data.clone()) - .build() - .expect("build should succeed"); - assert_eq!(blob.data(), data.as_slice()); - } -} From 762dfd2b5fca5c76c8abae91a78928184e10578c Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:04 +0700 Subject: [PATCH 05/12] test(core): remove inline tests from commit --- libvctrl_core/src/object/commit.rs | 115 ----------------------------- 1 file changed, 115 deletions(-) diff --git a/libvctrl_core/src/object/commit.rs b/libvctrl_core/src/object/commit.rs index 9208646..3e15948 100644 --- a/libvctrl_core/src/object/commit.rs +++ b/libvctrl_core/src/object/commit.rs @@ -80,118 +80,3 @@ impl CommitBuilder { } } } - -#[cfg(test)] -mod tests { - use super::*; - use libvctrl_handler::HASH_LENGTH; - - fn make_hash(fill: u8) -> Hash { - Hash::from_bytes(&vec![fill; HASH_LENGTH]).unwrap() - } - - fn make_user_id(name: &str, email: &str) -> UserID { - UserID::new(name.into(), email.into()).unwrap() - } - - #[test] - fn test_build_missing_tree() { - let result = CommitBuilder::new() - .author(make_user_id("A", "a@b.c")) - .committer(make_user_id("B", "b@c.d")) - .message("msg".into()) - .build(); - assert!(result.is_err(), "should fail without tree"); - } - - #[test] - fn test_build_missing_author() { - let result = CommitBuilder::new() - .tree(make_hash(0)) - .committer(make_user_id("B", "b@c.d")) - .message("msg".into()) - .build(); - assert!(result.is_err(), "should fail without author"); - } - - #[test] - fn test_build_missing_committer() { - let result = CommitBuilder::new() - .tree(make_hash(0)) - .author(make_user_id("A", "a@b.c")) - .message("msg".into()) - .build(); - assert!(result.is_err(), "should fail without committer"); - } - - #[test] - fn test_build_missing_message() { - let result = CommitBuilder::new() - .tree(make_hash(0)) - .author(make_user_id("A", "a@b.c")) - .committer(make_user_id("B", "b@c.d")) - .build(); - assert!(result.is_err(), "should fail without message"); - } - - #[test] - fn test_build_missing_all_required() { - let result = CommitBuilder::new().build(); - assert!(result.is_err(), "should fail with no fields set"); - } - - #[test] - fn test_build_success_without_meta() { - let result = CommitBuilder::new() - .tree(make_hash(1)) - .author(make_user_id("Alice", "alice@example.com")) - .committer(make_user_id("Bob", "bob@example.com")) - .message("initial commit".into()) - .build(); - assert!(result.is_ok(), "should succeed with all required fields"); - } - - #[test] - fn test_build_success_with_meta() { - let meta = CommitMeta::new(1700000000, 3600, Some("UTF-8".into())).unwrap(); - let result = CommitBuilder::new() - .tree(make_hash(1)) - .author(make_user_id("Alice", "alice@example.com")) - .committer(make_user_id("Bob", "bob@example.com")) - .message("initial commit".into()) - .meta(meta) - .build(); - assert!(result.is_ok(), "should succeed with meta"); - } - - #[test] - fn test_build_with_multiple_parents() { - let result = CommitBuilder::new() - .tree(make_hash(1)) - .parent(make_hash(2)) - .parent(make_hash(3)) - .parent(make_hash(4)) - .author(make_user_id("Alice", "alice@example.com")) - .committer(make_user_id("Bob", "bob@example.com")) - .message("merge commit".into()) - .build(); - assert!(result.is_ok(), "should succeed with multiple parents"); - let commit = result.unwrap(); - assert_eq!(commit.parents().len(), 3); - } - - #[test] - fn test_build_with_meta_preserves_timestamp() { - let meta = CommitMeta::new(9999999999, -7200, None).unwrap(); - let commit = CommitBuilder::new() - .tree(make_hash(1)) - .author(make_user_id("A", "a@b.c")) - .committer(make_user_id("B", "b@c.d")) - .message("ts test".into()) - .meta(meta) - .build() - .unwrap(); - assert_eq!(commit.meta().timestamp(), 9999999999); - assert_eq!(commit.meta().timezone_offset(), -7200); - } -} From 6f1df9b5079d782fb85cf89ad72cbbd7eca8c2a5 Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:04 +0700 Subject: [PATCH 06/12] test(core): remove inline tests from tag --- libvctrl_core/src/object/tag.rs | 83 --------------------------------- 1 file changed, 83 deletions(-) diff --git a/libvctrl_core/src/object/tag.rs b/libvctrl_core/src/object/tag.rs index 2f2f461..a5f81f7 100644 --- a/libvctrl_core/src/object/tag.rs +++ b/libvctrl_core/src/object/tag.rs @@ -72,86 +72,3 @@ impl TagBuilder { } } } - -#[cfg(test)] -mod tests { - use super::*; - use libvctrl_handler::HASH_LENGTH; - - fn make_hash(fill: u8) -> Hash { - Hash::from_bytes(&vec![fill; HASH_LENGTH]).unwrap() - } - - fn make_user_id(name: &str, email: &str) -> UserID { - UserID::new(name.into(), email.into()).unwrap() - } - - #[test] - fn test_build_missing_name() { - let result = TagBuilder::new().target(make_hash(0)).build(); - assert!(result.is_err(), "should fail without name"); - } - - #[test] - fn test_build_missing_target() { - let result = TagBuilder::new().name("v1.0".into()).build(); - assert!(result.is_err(), "should fail without target"); - } - - #[test] - fn test_build_missing_both() { - let result = TagBuilder::new().build(); - assert!(result.is_err(), "should fail without name and target"); - } - - #[test] - fn test_build_success_without_meta() { - let result = TagBuilder::new() - .name("v1.0".into()) - .target(make_hash(0xAA)) - .build(); - assert!(result.is_ok(), "should succeed with name and target"); - } - - #[test] - fn test_build_success_with_tagger_and_meta() { - let meta = CommitMeta::new(1700000000, 0, None).unwrap(); - let result = TagBuilder::new() - .name("release".into()) - .target(make_hash(0xBB)) - .tagger(make_user_id("Alice", "alice@example.com")) - .message("v1.0 release".into()) - .meta(meta) - .build(); - assert!(result.is_ok(), "should succeed with all fields"); - let tag = result.unwrap(); - assert_eq!(tag.name(), "release"); - assert!(tag.tagger().is_some()); - assert_eq!(tag.tagger().unwrap().name(), "Alice"); - assert_eq!(tag.message(), "v1.0 release"); - } - - #[test] - fn test_build_default_message_when_none() { - let result = TagBuilder::new() - .name("v2.0".into()) - .target(make_hash(0xCC)) - .build(); - assert!(result.is_ok()); - let tag = result.unwrap(); - assert_eq!(tag.message(), "", "message should default to empty string"); - } - - #[test] - fn test_build_without_tagger() { - let meta = CommitMeta::new(1700000000, 0, Some("UTF-8".into())).unwrap(); - let result = TagBuilder::new() - .name("lightweight".into()) - .target(make_hash(0xDD)) - .meta(meta) - .build(); - assert!(result.is_ok()); - let tag = result.unwrap(); - assert!(tag.tagger().is_none(), "tagger should be None when not set"); - } -} From 8cd3b42ab8896e35c9ee686049673a7a0d5b2d4b Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:05 +0700 Subject: [PATCH 07/12] test(core): remove inline tests from tree --- libvctrl_core/src/object/tree.rs | 86 -------------------------------- 1 file changed, 86 deletions(-) diff --git a/libvctrl_core/src/object/tree.rs b/libvctrl_core/src/object/tree.rs index 3406ee2..87bf772 100644 --- a/libvctrl_core/src/object/tree.rs +++ b/libvctrl_core/src/object/tree.rs @@ -52,89 +52,3 @@ impl TreeEntryBuilder { TreeEntry::new(self.name, self.kind, self.hash) } } - -#[cfg(test)] -mod tests { - use super::*; - use libvctrl_handler::HASH_LENGTH; - - fn make_hash(fill: u8) -> Hash { - Hash::from_bytes(&vec![fill; HASH_LENGTH]).unwrap() - } - - #[test] - fn test_tree_builder_build_empty() { - let result = TreeBuilder::new().build(); - assert!(result.is_ok(), "empty tree should be valid"); - let tree = result.unwrap(); - assert_eq!(tree.entries().len(), 0); - } - - #[test] - fn test_tree_builder_build_with_entries_via_entry_method() { - let entry = TreeEntry::new("README.md".into(), EntryKind::Blob, make_hash(0x01)).unwrap(); - let result = TreeBuilder::new().entry(entry).build(); - assert!(result.is_ok()); - let tree = result.unwrap(); - assert_eq!(tree.entries().len(), 1); - assert_eq!(tree.entries()[0].name(), "README.md"); - } - - #[test] - fn test_tree_builder_build_with_multiple_entries() { - let e1 = TreeEntry::new("src".into(), EntryKind::Tree, make_hash(0x01)).unwrap(); - let e2 = TreeEntry::new("Cargo.toml".into(), EntryKind::Blob, make_hash(0x02)).unwrap(); - let result = TreeBuilder::new().entry(e1).entry(e2).build(); - assert!(result.is_ok()); - let tree = result.unwrap(); - assert_eq!(tree.entries().len(), 2); - } - - #[test] - fn test_tree_builder_add_entry_success() { - let result = TreeBuilder::new() - .add_entry("main.rs".into(), EntryKind::Blob, make_hash(0x03)) - .and_then(|b| b.build()); - assert!(result.is_ok()); - let tree = result.unwrap(); - assert_eq!(tree.entries()[0].name(), "main.rs"); - assert_eq!(tree.entries()[0].kind(), EntryKind::Blob); - } - - #[test] - fn test_tree_builder_add_entry_chaining() { - let result = TreeBuilder::new() - .add_entry("a.txt".into(), EntryKind::Blob, make_hash(0x10)) - .and_then(|b| b.add_entry("b.txt".into(), EntryKind::Blob, make_hash(0x20))) - .and_then(|b| b.build()); - assert!(result.is_ok()); - let tree = result.unwrap(); - assert_eq!(tree.entries().len(), 2); - } - - #[test] - fn test_tree_entry_builder_build_success() { - let result = - TreeEntryBuilder::new("lib.rs".into(), EntryKind::Blob, make_hash(0x42)).build(); - assert!(result.is_ok()); - let entry = result.unwrap(); - assert_eq!(entry.name(), "lib.rs"); - assert_eq!(entry.kind(), EntryKind::Blob); - } - - #[test] - fn test_tree_entry_builder_all_kinds() { - for kind in [ - EntryKind::Blob, - EntryKind::Executable, - EntryKind::Symlink, - EntryKind::Tree, - EntryKind::Submodule, - ] { - let result = - TreeEntryBuilder::new(format!("item_{kind:?}"), kind, make_hash(0xFF)).build(); - assert!(result.is_ok(), "should succeed for kind {kind:?}"); - assert_eq!(result.unwrap().kind(), kind); - } - } -} From 6feb8e3dc19f8069d4b218bd662e791aee66ac9e Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:05 +0700 Subject: [PATCH 08/12] test(core): remove inline tests from memory store --- libvctrl_core/src/store/memory.rs | 115 ------------------------------ 1 file changed, 115 deletions(-) diff --git a/libvctrl_core/src/store/memory.rs b/libvctrl_core/src/store/memory.rs index 84fe30e..8e01e40 100644 --- a/libvctrl_core/src/store/memory.rs +++ b/libvctrl_core/src/store/memory.rs @@ -39,118 +39,3 @@ impl ObjectStore for MemoryStore { Ok(self.objects.contains_key(hash)) } } - -#[cfg(test)] -mod tests { - use super::*; - use libvctrl_handler::HASH_LENGTH; - - fn make_hash(fill: u8) -> Hash { - Hash::from_bytes(&vec![fill; HASH_LENGTH]).unwrap() - } - - #[test] - fn test_put_and_get() { - let mut store = MemoryStore::new(); - let hash = make_hash(0x01); - let data = b"hello world"; - assert!(store.put(&hash, data).is_ok()); - - let get_result = store.get(&hash); - assert!(get_result.is_ok(), "should retrieve stored object"); - - let mut reader = get_result.unwrap(); - let mut retrieved = Vec::new(); - Read::read_to_end(&mut reader, &mut retrieved).unwrap(); - assert_eq!(retrieved, data, "retrieved data must match original"); - } - - #[test] - fn test_get_not_found() { - let store = MemoryStore::new(); - let hash = make_hash(0xFF); - let result = store.get(&hash); - assert!(result.is_err(), "should error for missing object"); - } - - #[test] - fn test_exists_false_then_true() { - let mut store = MemoryStore::new(); - let hash = make_hash(0x10); - assert_eq!( - store.exists(&hash).unwrap(), - false, - "should not exist before put" - ); - store.put(&hash, b"data").unwrap(); - assert_eq!(store.exists(&hash).unwrap(), true, "should exist after put"); - } - - #[test] - fn test_delete_existing() { - let mut store = MemoryStore::new(); - let hash = make_hash(0x20); - store.put(&hash, b"to delete").unwrap(); - assert!(store.exists(&hash).unwrap()); - store.delete(&hash).unwrap(); - assert!( - !store.exists(&hash).unwrap(), - "should not exist after delete" - ); - } - - #[test] - fn test_delete_nonexistent() { - let mut store = MemoryStore::new(); - let hash = make_hash(0x30); - let result = store.delete(&hash); - assert!(result.is_ok(), "deleting nonexistent key should not error"); - } - - #[test] - fn test_overwrite() { - let mut store = MemoryStore::new(); - let hash = make_hash(0x40); - store.put(&hash, b"first version").unwrap(); - store.put(&hash, b"second version").unwrap(); - - let mut reader = store.get(&hash).unwrap(); - let mut retrieved = Vec::new(); - Read::read_to_end(&mut reader, &mut retrieved).unwrap(); - assert_eq!( - retrieved, b"second version", - "should return the most recently put data" - ); - } - - #[test] - fn test_put_empty_data() { - let mut store = MemoryStore::new(); - let hash = make_hash(0x50); - store.put(&hash, b"").unwrap(); - let mut reader = store.get(&hash).unwrap(); - let mut retrieved = Vec::new(); - Read::read_to_end(&mut reader, &mut retrieved).unwrap(); - assert_eq!(retrieved, b"", "empty data should be stored and retrieved"); - } - - #[test] - fn test_multiple_objects() { - let mut store = MemoryStore::new(); - let h1 = make_hash(0x01); - let h2 = make_hash(0x02); - let h3 = make_hash(0x03); - store.put(&h1, b"aaa").unwrap(); - store.put(&h2, b"bbb").unwrap(); - store.put(&h3, b"ccc").unwrap(); - - assert!(store.exists(&h1).unwrap()); - assert!(store.exists(&h2).unwrap()); - assert!(store.exists(&h3).unwrap()); - - store.delete(&h2).unwrap(); - assert!(store.exists(&h1).unwrap()); - assert!(!store.exists(&h2).unwrap()); - assert!(store.exists(&h3).unwrap()); - } -} From 94c6f37ca9767e2dab59d8843a698069ee27cc38 Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:05 +0700 Subject: [PATCH 09/12] test(core): remove inline tests from ref_store --- libvctrl_core/src/store/ref_store.rs | 125 --------------------------- 1 file changed, 125 deletions(-) diff --git a/libvctrl_core/src/store/ref_store.rs b/libvctrl_core/src/store/ref_store.rs index 3c9491b..fce63e5 100644 --- a/libvctrl_core/src/store/ref_store.rs +++ b/libvctrl_core/src/store/ref_store.rs @@ -44,128 +44,3 @@ impl RefStore for MemoryRefStore { Ok(names.into_iter().map(Ok).collect::>().into_iter()) } } - -#[cfg(test)] -mod tests { - use super::*; - use libvctrl_handler::HASH_LENGTH; - - fn make_hash(fill: u8) -> Hash { - Hash::from_bytes(&vec![fill; HASH_LENGTH]).unwrap() - } - - #[test] - fn test_set_and_get() { - let mut store = MemoryRefStore::new(); - let hash = make_hash(0x01); - assert!(store.set_ref("refs/heads/main", &hash).is_ok()); - - let result = store.get_ref("refs/heads/main"); - assert!(result.is_ok()); - assert_eq!( - result.unwrap(), - hash, - "retrieved hash must match stored hash" - ); - } - - #[test] - fn test_get_not_found() { - let store = MemoryRefStore::new(); - let result = store.get_ref("refs/heads/nonexistent"); - assert!(result.is_err(), "should error for missing ref"); - } - - #[test] - fn test_delete_existing() { - let mut store = MemoryRefStore::new(); - let hash = make_hash(0x10); - store.set_ref("refs/tags/v1", &hash).unwrap(); - assert!(store.get_ref("refs/tags/v1").is_ok()); - store.delete_ref("refs/tags/v1").unwrap(); - assert!(store.get_ref("refs/tags/v1").is_err()); - } - - #[test] - fn test_delete_nonexistent() { - let mut store = MemoryRefStore::new(); - let result = store.delete_ref("refs/heads/nope"); - assert!(result.is_ok(), "deleting nonexistent ref should not error"); - } - - #[test] - fn test_list_refs_empty() { - let store = MemoryRefStore::new(); - let refs: Vec = store - .list_refs() - .unwrap() - .collect::, _>>() - .unwrap(); - assert!(refs.is_empty(), "new store should have no refs"); - } - - #[test] - fn test_list_refs_sorted() { - let mut store = MemoryRefStore::new(); - let h1 = make_hash(0x01); - let h2 = make_hash(0x02); - let h3 = make_hash(0x03); - store.set_ref("refs/heads/main", &h1).unwrap(); - store.set_ref("refs/heads/feature", &h2).unwrap(); - store.set_ref("refs/tags/v1.0", &h3).unwrap(); - - let refs: Vec = store - .list_refs() - .unwrap() - .collect::, _>>() - .unwrap(); - assert_eq!( - refs, - vec![ - "refs/heads/feature".to_string(), - "refs/heads/main".to_string(), - "refs/tags/v1.0".to_string(), - ], - "refs should be returned in sorted order" - ); - } - - #[test] - fn test_set_overwrite() { - let mut store = MemoryRefStore::new(); - let h1 = make_hash(0xAA); - let h2 = make_hash(0xBB); - store.set_ref("refs/heads/main", &h1).unwrap(); - store.set_ref("refs/heads/main", &h2).unwrap(); - assert_eq!( - store.get_ref("refs/heads/main").unwrap(), - h2, - "should return the most recently set hash" - ); - } - - #[test] - fn test_set_invalid_ref_name() { - let mut store = MemoryRefStore::new(); - let hash = make_hash(0x00); - let result = store.set_ref("invalid name with spaces", &hash); - assert!(result.is_err(), "ref name with spaces should be rejected"); - } - - #[test] - fn test_set_multiple_refs_independent() { - let mut store = MemoryRefStore::new(); - let h_main = make_hash(0x01); - let h_dev = make_hash(0x02); - store.set_ref("refs/heads/main", &h_main).unwrap(); - store.set_ref("refs/heads/dev", &h_dev).unwrap(); - - assert_eq!(store.get_ref("refs/heads/main").unwrap(), h_main); - assert_eq!(store.get_ref("refs/heads/dev").unwrap(), h_dev); - assert_eq!( - store.list_refs().unwrap().count(), - 2, - "should have exactly 2 refs" - ); - } -} From ea7b0588752e31bab1748f2aeb1721192308b21c Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:05 +0700 Subject: [PATCH 10/12] test(core): remove builder_api integration tests --- libvctrl_core/tests/builder_api.rs | 48 ------------------------------ 1 file changed, 48 deletions(-) delete mode 100644 libvctrl_core/tests/builder_api.rs diff --git a/libvctrl_core/tests/builder_api.rs b/libvctrl_core/tests/builder_api.rs deleted file mode 100644 index 4821d39..0000000 --- a/libvctrl_core/tests/builder_api.rs +++ /dev/null @@ -1,48 +0,0 @@ -use libvctrl_core::object::{BlobBuilder, CommitBuilder, TagBuilder, TreeBuilder}; - -mod common; - -#[test] -fn test_blob_builder_build_success_via_public_api() { - let result = BlobBuilder::new().with_data(vec![1, 2, 3]).build(); - assert!( - result.is_ok(), - "BlobBuilder should succeed with valid data via public API" - ); -} - -#[test] -fn test_commit_builder_missing_tree_via_public_api() { - let result = CommitBuilder::new().build(); - assert!( - result.is_err(), - "CommitBuilder should fail without tree via public API" - ); -} - -#[test] -fn test_tag_builder_missing_name_via_public_api() { - let result = TagBuilder::new().build(); - assert!( - result.is_err(), - "TagBuilder should fail without name via public API" - ); -} - -#[test] -fn test_tag_builder_missing_target_via_public_api() { - let result = TagBuilder::new().name("v1.0").build(); - assert!( - result.is_err(), - "TagBuilder should fail without target via public API" - ); -} - -#[test] -fn test_tree_builder_build_empty_via_public_api() { - let result = TreeBuilder::new().build(); - assert!( - result.is_ok(), - "TreeBuilder should succeed with empty entries via public API" - ); -} From c4071563dfb2eaa801ee382529d600fd62a3bcda Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:05 +0700 Subject: [PATCH 11/12] test(core): remove codec_roundtrip integration tests --- libvctrl_core/tests/codec_roundtrip.rs | 68 -------------------------- 1 file changed, 68 deletions(-) delete mode 100644 libvctrl_core/tests/codec_roundtrip.rs diff --git a/libvctrl_core/tests/codec_roundtrip.rs b/libvctrl_core/tests/codec_roundtrip.rs deleted file mode 100644 index 2e5938c..0000000 --- a/libvctrl_core/tests/codec_roundtrip.rs +++ /dev/null @@ -1,68 +0,0 @@ -use libvctrl_core::codec::{BinaryDecoder, BinaryEncoder, VERSION}; -use libvctrl_core::object::BlobBuilder; -use std::io::Cursor; - -mod common; - -fn encode_to_vec(encode_fn: F) -> Vec -where - W: std::io::Write + Send, - F: FnOnce(&mut W) -> Result<(), libvctrl_core::codec::binary_encoder::VctrlError>, -{ - let mut buf = Cursor::new(Vec::new()); - encode_fn(&mut buf).unwrap(); - buf.into_inner() -} - -#[test] -fn test_blob_roundtrip() { - let original_data = vec![0x01, 0x02, 0x03, 0x04, 0x05]; - let blob = BlobBuilder::new() - .with_data(original_data.clone()) - .build() - .expect("blob build should succeed"); - - let encoded = encode_to_vec(|w| BinaryEncoder.encode_blob(&blob, w)); - assert_eq!(encoded[0], VERSION, "first byte should be version"); - - let decoded = BinaryDecoder - .decode_blob(Cursor::new(encoded)) - .expect("decode should succeed"); - assert_eq!( - decoded.data(), - original_data.as_slice(), - "roundtrip blob data should match original" - ); -} - -#[test] -fn test_blob_empty_roundtrip() { - let blob = BlobBuilder::new() - .with_data(vec![]) - .build() - .expect("empty blob build should succeed"); - - let encoded = encode_to_vec(|w| BinaryEncoder.encode_blob(&blob, w)); - let decoded = BinaryDecoder - .decode_blob(Cursor::new(encoded)) - .expect("decode empty blob should succeed"); - assert!( - decoded.data().is_empty(), - "roundtrip empty blob should have empty data" - ); -} - -#[test] -fn test_blob_large_roundtrip() { - let original_data = vec![0x42u8; 8192]; - let blob = BlobBuilder::new() - .with_data(original_data.clone()) - .build() - .expect("large blob build should succeed"); - - let encoded = encode_to_vec(|w| BinaryEncoder.encode_blob(&blob, w)); - let decoded = BinaryDecoder - .decode_blob(Cursor::new(encoded)) - .expect("decode large blob should succeed"); - assert_eq!(decoded.data(), original_data.as_slice()); -} From 34231f4aabd21aff6d3cd8dd6395d0ec0c0433ee Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 19:34:05 +0700 Subject: [PATCH 12/12] test(core): remove common test utilities --- libvctrl_core/tests/common/mod.rs | 1 - 1 file changed, 1 deletion(-) delete mode 100644 libvctrl_core/tests/common/mod.rs diff --git a/libvctrl_core/tests/common/mod.rs b/libvctrl_core/tests/common/mod.rs deleted file mode 100644 index f371760..0000000 --- a/libvctrl_core/tests/common/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub fn setup() {}