diff --git a/libvctrl_core/src/codec/binary_decoder.rs b/libvctrl_core/src/codec/binary_decoder.rs index 704bc03..6fa9293 100644 --- a/libvctrl_core/src/codec/binary_decoder.rs +++ b/libvctrl_core/src/codec/binary_decoder.rs @@ -402,8 +402,6 @@ mod tests { vec![fill; HASH_LENGTH] } - // --- Private helper tests --- - #[test] fn test_check_version_missing_byte() { let result = BinaryDecoder::check_version(&[]); @@ -509,8 +507,6 @@ mod tests { ); } - // --- decode_blob tests --- - #[test] fn test_decode_blob_valid() { let payload = b"hello world"; @@ -574,8 +570,6 @@ mod tests { assert!(result.is_err()); } - // --- decode_tree tests --- - #[test] fn test_decode_tree_valid_single_entry() { let hb = hash_bytes(0xAB); @@ -584,7 +578,7 @@ mod tests { data.extend_from_slice(&1u32.to_le_bytes()); data.push(4); data.extend_from_slice(b"file"); - data.push(0); // Blob + data.push(0); data.extend_from_slice(&hb); let result = BinaryDecoder.decode_tree(Cursor::new(data)); @@ -613,15 +607,13 @@ mod tests { 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.push(3); data.extend_from_slice(&hb1); - // Entry 2 data.push(9); data.extend_from_slice(b"Cargo.toml"); - data.push(0); // Blob + data.push(0); data.extend_from_slice(&hb2); let result = BinaryDecoder.decode_tree(Cursor::new(data)); @@ -642,7 +634,7 @@ mod tests { data.extend_from_slice(&1u32.to_le_bytes()); data.push(1); data.push(b'x'); - data.push(99); // unknown kind + data.push(99); data.extend_from_slice(&hb); let result = BinaryDecoder.decode_tree(Cursor::new(data)); @@ -659,7 +651,7 @@ mod tests { data.push(b'x'); data.push(0); data.extend_from_slice(&hb); - data.push(0xFF); // trailing + data.push(0xFF); let result = BinaryDecoder.decode_tree(Cursor::new(data)); assert!(result.is_err()); @@ -667,7 +659,7 @@ mod tests { #[test] fn test_decode_tree_all_known_kinds() { - let kinds = [0u8, 1, 2, 3, 4]; // Blob, Executable, Symlink, Tree, Submodule + let kinds = [0u8, 1, 2, 3, 4]; let mut data = Vec::new(); data.push(EXPECTED_VERSION); data.extend_from_slice(&(kinds.len() as u32).to_le_bytes()); @@ -683,8 +675,6 @@ mod tests { assert!(result.is_ok(), "should decode all known entry kinds"); } - // --- decode_commit tests --- - fn build_valid_commit_bytes( tree_fill: u8, parents: &[u8], @@ -736,7 +726,7 @@ mod tests { "Bob", "b@c.d", "init", - 1700000000, + 1_700_000_000, 0, None, ); @@ -749,7 +739,7 @@ mod tests { 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().timestamp(), 1_700_000_000); assert_eq!(commit.meta().timezone_offset(), 0); assert!(commit.meta().encoding().is_none()); } @@ -764,7 +754,7 @@ mod tests { "Bob", "bob@ex.com", "merge", - 1700000000, + 1_700_000_000, 3600, Some("UTF-8"), ); @@ -804,8 +794,6 @@ mod tests { assert_eq!(result.unwrap().message(), ""); } - // --- decode_tag tests --- - fn build_valid_tag_bytes( name: &str, target_fill: u8, @@ -851,7 +839,7 @@ mod tests { 0x10, Some(("Alice", "alice@ex.com")), "release", - 1700000000, + 1_700_000_000, 0, None, ); @@ -867,7 +855,7 @@ mod tests { #[test] fn test_decode_tag_no_tagger() { - let data = build_valid_tag_bytes("v2.0", 0x20, None, "", 1700000000, 0, None); + let data = build_valid_tag_bytes("v2.0", 0x20, None, "", 1_700_000_000, 0, None); let result = BinaryDecoder.decode_tag(Cursor::new(data)); assert!(result.is_ok()); let tag = result.unwrap(); @@ -883,7 +871,7 @@ mod tests { 0x30, Some(("Bob", "bob@ex.com")), "annotated", - 1700000000, + 1_700_000_000, -3600, Some("UTF-8"), ); @@ -897,9 +885,9 @@ mod tests { #[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 pos = 1 + 4 + HASH_LENGTH; let mut mutable_data = data; - mutable_data[pos] = 5; // invalid tagger presence byte + mutable_data[pos] = 5; let result = BinaryDecoder.decode_tag(Cursor::new(mutable_data)); assert!(result.is_err()); } diff --git a/libvctrl_core/src/codec/binary_encoder.rs b/libvctrl_core/src/codec/binary_encoder.rs index 6bf6721..2f4352f 100644 --- a/libvctrl_core/src/codec/binary_encoder.rs +++ b/libvctrl_core/src/codec/binary_encoder.rs @@ -241,7 +241,8 @@ impl Encoder for BinaryEncoder { #[cfg(test)] mod tests { use super::*; - use libvctrl_handler::{CommitMeta, HASH_LENGTH, Hash, UserID}; + use crate::codec::BinaryDecoder; + use libvctrl_handler::{CommitMeta, Decoder, HASH_LENGTH, Hash, TreeEntry, UserID}; use std::io::Cursor; fn make_hash(fill: u8) -> Hash { @@ -299,9 +300,9 @@ mod tests { let mut expected = Vec::new(); expected.push(VERSION); expected.extend_from_slice(&1u32.to_le_bytes()); - expected.push(6); // "README" length + expected.push(6); expected.extend_from_slice(b"README"); - expected.push(0); // Blob + expected.push(0); expected.extend_from_slice(&hash_bytes(0xAB)); assert_eq!(encoded, expected); } @@ -333,11 +334,11 @@ mod tests { expected.extend_from_slice(&2u32.to_le_bytes()); expected.push(3); expected.extend_from_slice(b"src"); - expected.push(3); // Tree + expected.push(3); expected.extend_from_slice(&hash_bytes(0x01)); expected.push(3); expected.extend_from_slice(b"run"); - expected.push(1); // Executable + expected.push(1); expected.extend_from_slice(&hash_bytes(0x02)); assert_eq!(encoded, expected); } @@ -350,7 +351,7 @@ mod tests { make_user_id("Alice", "a@b.c"), make_user_id("Bob", "b@c.d"), "init".into(), - make_meta(1700000000, 0, None), + make_meta(1_700_000_000, 0, None), ) .unwrap(); let mut buf = Cursor::new(Vec::new()); @@ -371,7 +372,7 @@ mod tests { 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(&1_700_000_000_i64.to_le_bytes()); expected.extend_from_slice(&0i16.to_le_bytes()); expected.push(0); assert_eq!(encoded, expected); @@ -385,7 +386,7 @@ mod tests { make_user_id("A", "a@b.c"), make_user_id("B", "b@c.d"), "msg".into(), - make_meta(1700000000, 3600, Some("UTF-8")), + make_meta(1_700_000_000, 3600, Some("UTF-8")), ) .unwrap(); let mut buf = Cursor::new(Vec::new()); @@ -406,7 +407,7 @@ mod tests { 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(&1_700_000_000_i64.to_le_bytes()); expected.extend_from_slice(&3600i16.to_le_bytes()); expected.push(5); expected.extend_from_slice(b"UTF-8"); @@ -457,7 +458,7 @@ mod tests { make_hash(0x10), Some(make_user_id("Alice", "alice@ex.com")), "release".into(), - make_meta(1700000000, 0, None), + make_meta(1_700_000_000, 0, None), ) .unwrap(); let mut buf = Cursor::new(Vec::new()); @@ -466,17 +467,17 @@ mod tests { let mut expected = Vec::new(); expected.push(VERSION); - expected.push(4); // "v1.0" length + expected.push(4); expected.extend_from_slice(b"v1.0"); expected.extend_from_slice(&hash_bytes(0x10)); - expected.push(1); // has tagger + expected.push(1); 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(&1_700_000_000_i64.to_le_bytes()); expected.extend_from_slice(&0i16.to_le_bytes()); expected.push(0); assert_eq!(encoded, expected); @@ -489,7 +490,7 @@ mod tests { make_hash(0x20), None, "".into(), - make_meta(1700000000, 0, None), + make_meta(1_700_000_000, 0, None), ) .unwrap(); let mut buf = Cursor::new(Vec::new()); @@ -501,9 +502,9 @@ mod tests { expected.push(4); expected.extend_from_slice(b"v2.0"); expected.extend_from_slice(&hash_bytes(0x20)); - expected.push(0); // no tagger + expected.push(0); expected.extend_from_slice(&0u32.to_le_bytes()); - expected.extend_from_slice(&1700000000i64.to_le_bytes()); + expected.extend_from_slice(&1_700_000_000_i64.to_le_bytes()); expected.extend_from_slice(&0i16.to_le_bytes()); expected.push(0); assert_eq!(encoded, expected); @@ -516,7 +517,7 @@ mod tests { make_hash(0x30), Some(make_user_id("B", "b@c.d")), "tag".into(), - make_meta(1700000000, -3600, Some("UTF-8")), + make_meta(1_700_000_000, -3600, Some("UTF-8")), ) .unwrap(); let mut buf = Cursor::new(Vec::new()); @@ -525,7 +526,7 @@ mod tests { let mut expected = Vec::new(); expected.push(VERSION); - expected.push(2); // "v3" length + expected.push(2); expected.extend_from_slice(b"v3"); expected.extend_from_slice(&hash_bytes(0x30)); expected.push(1); @@ -535,10 +536,156 @@ mod tests { 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(&1_700_000_000_i64.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_blob_roundtrip() { + let original_data = vec![0x01, 0x02, 0x03, 0x04, 0x05]; + let blob = Blob::new(original_data.clone()).unwrap(); + let mut buf = Cursor::new(Vec::new()); + BinaryEncoder.encode_blob(&blob, &mut buf).unwrap(); + let encoded = buf.into_inner(); + + 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 = Blob::new(vec![]).unwrap(); + let mut buf = Cursor::new(Vec::new()); + BinaryEncoder.encode_blob(&blob, &mut buf).unwrap(); + let encoded = buf.into_inner(); + + 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 = Blob::new(original_data.clone()).unwrap(); + let mut buf = Cursor::new(Vec::new()); + BinaryEncoder.encode_blob(&blob, &mut buf).unwrap(); + let encoded = buf.into_inner(); + + let decoded = BinaryDecoder + .decode_blob(Cursor::new(encoded)) + .expect("decode large blob should succeed"); + assert_eq!(decoded.data(), original_data.as_slice()); + } + + #[test] + fn test_tree_roundtrip() { + let e1 = TreeEntry::new("file.txt".into(), EntryKind::Blob, make_hash(0x01)).unwrap(); + let e2 = TreeEntry::new("src".into(), EntryKind::Tree, 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 decoded = BinaryDecoder + .decode_tree(Cursor::new(encoded)) + .expect("decode tree should succeed"); + assert_eq!(decoded.entries().len(), 2); + assert_eq!(decoded.entries()[0].name(), "file.txt"); + assert_eq!(decoded.entries()[0].kind(), EntryKind::Blob); + assert_eq!(decoded.entries()[1].name(), "src"); + assert_eq!(decoded.entries()[1].kind(), EntryKind::Tree); + } + + #[test] + fn test_commit_roundtrip() { + let commit = Commit::with_meta( + make_hash(0x01), + vec![make_hash(0x02)], + make_user_id("Alice", "alice@ex.com"), + make_user_id("Bob", "bob@ex.com"), + "roundtrip test".into(), + make_meta(1_700_000_000, 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 decoded = BinaryDecoder + .decode_commit(Cursor::new(encoded)) + .expect("decode commit should succeed"); + assert_eq!(decoded.parents().len(), 1); + assert_eq!(decoded.author().name(), "Alice"); + assert_eq!(decoded.committer().name(), "Bob"); + assert_eq!(decoded.message(), "roundtrip test"); + assert_eq!(decoded.meta().timestamp(), 1_700_000_000); + assert_eq!(decoded.meta().timezone_offset(), 3600); + assert_eq!(decoded.meta().encoding(), Some("UTF-8")); + } + + #[test] + fn test_tag_roundtrip() { + let tag = Tag::with_meta( + "v1.0".into(), + make_hash(0x10), + Some(make_user_id("Alice", "alice@ex.com")), + "release tag".into(), + make_meta(1_700_000_000, 0, None), + ) + .unwrap(); + + let mut buf = Cursor::new(Vec::new()); + BinaryEncoder.encode_tag(&tag, &mut buf).unwrap(); + let encoded = buf.into_inner(); + + let decoded = BinaryDecoder + .decode_tag(Cursor::new(encoded)) + .expect("decode tag should succeed"); + assert_eq!(decoded.name(), "v1.0"); + assert!(decoded.tagger().is_some()); + assert_eq!(decoded.tagger().unwrap().name(), "Alice"); + assert_eq!(decoded.message(), "release tag"); + assert!(decoded.meta().encoding().is_none()); + } + + #[test] + fn test_tag_roundtrip_no_tagger() { + let tag = Tag::with_meta( + "v2.0".into(), + make_hash(0x20), + None, + "".into(), + make_meta(1_700_000_000, 0, None), + ) + .unwrap(); + + let mut buf = Cursor::new(Vec::new()); + BinaryEncoder.encode_tag(&tag, &mut buf).unwrap(); + let encoded = buf.into_inner(); + + let decoded = BinaryDecoder + .decode_tag(Cursor::new(encoded)) + .expect("decode lightweight tag should succeed"); + assert_eq!(decoded.name(), "v2.0"); + assert!(decoded.tagger().is_none()); + assert_eq!(decoded.message(), ""); + } } diff --git a/libvctrl_core/src/object/commit.rs b/libvctrl_core/src/object/commit.rs index 9208646..5af34c4 100644 --- a/libvctrl_core/src/object/commit.rs +++ b/libvctrl_core/src/object/commit.rs @@ -99,7 +99,7 @@ mod tests { let result = CommitBuilder::new() .author(make_user_id("A", "a@b.c")) .committer(make_user_id("B", "b@c.d")) - .message("msg".into()) + .message("msg") .build(); assert!(result.is_err(), "should fail without tree"); } @@ -109,7 +109,7 @@ mod tests { let result = CommitBuilder::new() .tree(make_hash(0)) .committer(make_user_id("B", "b@c.d")) - .message("msg".into()) + .message("msg") .build(); assert!(result.is_err(), "should fail without author"); } @@ -119,7 +119,7 @@ mod tests { let result = CommitBuilder::new() .tree(make_hash(0)) .author(make_user_id("A", "a@b.c")) - .message("msg".into()) + .message("msg") .build(); assert!(result.is_err(), "should fail without committer"); } @@ -146,19 +146,19 @@ mod tests { .tree(make_hash(1)) .author(make_user_id("Alice", "alice@example.com")) .committer(make_user_id("Bob", "bob@example.com")) - .message("initial commit".into()) + .message("initial commit") .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 meta = CommitMeta::new(1_700_000_000, 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()) + .message("initial commit") .meta(meta) .build(); assert!(result.is_ok(), "should succeed with meta"); @@ -173,7 +173,7 @@ mod tests { .parent(make_hash(4)) .author(make_user_id("Alice", "alice@example.com")) .committer(make_user_id("Bob", "bob@example.com")) - .message("merge commit".into()) + .message("merge commit") .build(); assert!(result.is_ok(), "should succeed with multiple parents"); let commit = result.unwrap(); @@ -182,16 +182,16 @@ mod tests { #[test] fn test_build_with_meta_preserves_timestamp() { - let meta = CommitMeta::new(9999999999, -7200, None).unwrap(); + let meta = CommitMeta::new(9_999_999_999, -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()) + .message("ts test") .meta(meta) .build() .unwrap(); - assert_eq!(commit.meta().timestamp(), 9999999999); + assert_eq!(commit.meta().timestamp(), 9_999_999_999); assert_eq!(commit.meta().timezone_offset(), -7200); } } diff --git a/libvctrl_core/src/object/tag.rs b/libvctrl_core/src/object/tag.rs index 2f2f461..a404290 100644 --- a/libvctrl_core/src/object/tag.rs +++ b/libvctrl_core/src/object/tag.rs @@ -94,7 +94,7 @@ mod tests { #[test] fn test_build_missing_target() { - let result = TagBuilder::new().name("v1.0".into()).build(); + let result = TagBuilder::new().name("v1.0").build(); assert!(result.is_err(), "should fail without target"); } @@ -107,7 +107,7 @@ mod tests { #[test] fn test_build_success_without_meta() { let result = TagBuilder::new() - .name("v1.0".into()) + .name("v1.0") .target(make_hash(0xAA)) .build(); assert!(result.is_ok(), "should succeed with name and target"); @@ -115,12 +115,12 @@ mod tests { #[test] fn test_build_success_with_tagger_and_meta() { - let meta = CommitMeta::new(1700000000, 0, None).unwrap(); + let meta = CommitMeta::new(1_700_000_000, 0, None).unwrap(); let result = TagBuilder::new() - .name("release".into()) + .name("release") .target(make_hash(0xBB)) .tagger(make_user_id("Alice", "alice@example.com")) - .message("v1.0 release".into()) + .message("v1.0 release") .meta(meta) .build(); assert!(result.is_ok(), "should succeed with all fields"); @@ -134,7 +134,7 @@ mod tests { #[test] fn test_build_default_message_when_none() { let result = TagBuilder::new() - .name("v2.0".into()) + .name("v2.0") .target(make_hash(0xCC)) .build(); assert!(result.is_ok()); @@ -144,9 +144,9 @@ mod tests { #[test] fn test_build_without_tagger() { - let meta = CommitMeta::new(1700000000, 0, Some("UTF-8".into())).unwrap(); + let meta = CommitMeta::new(1_700_000_000, 0, Some("UTF-8".into())).unwrap(); let result = TagBuilder::new() - .name("lightweight".into()) + .name("lightweight") .target(make_hash(0xDD)) .meta(meta) .build(); diff --git a/libvctrl_core/tests/builder_api.rs b/libvctrl_core/tests/builder_api.rs index 4821d39..4cdc790 100644 --- a/libvctrl_core/tests/builder_api.rs +++ b/libvctrl_core/tests/builder_api.rs @@ -1,5 +1,10 @@ +use libvctrl_handler as _; +use libvctrl_sha512 as _; +use proptest as _; + use libvctrl_core::object::{BlobBuilder, CommitBuilder, TagBuilder, TreeBuilder}; +#[allow(dead_code)] mod common; #[test] diff --git a/libvctrl_core/tests/common/mod.rs b/libvctrl_core/tests/common/mod.rs index f371760..e27e377 100644 --- a/libvctrl_core/tests/common/mod.rs +++ b/libvctrl_core/tests/common/mod.rs @@ -1 +1,2 @@ -pub fn setup() {} +#[allow(dead_code, clippy::missing_const_for_fn)] +pub(crate) fn setup() {}