From 60057b0bb8f5ec4febe48b3eef5a8556f13c03d0 Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 20:33:32 +0700 Subject: [PATCH 1/2] style(root): remove unnecessary blank lines --- libvctrl/src/lib.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/libvctrl/src/lib.rs b/libvctrl/src/lib.rs index e669390..c67ff8d 100644 --- a/libvctrl/src/lib.rs +++ b/libvctrl/src/lib.rs @@ -2,23 +2,15 @@ use proptest as _; pub use libvctrl_core as reference; - pub use libvctrl_handler as handler; - pub use libvctrl_sha512 as crypto; pub use handler::constants; - pub use handler::enums; - pub use handler::errors; - pub use handler::macros; - pub use handler::traits; - pub use handler::types; - pub use handler::validation; pub use handler::{ @@ -39,27 +31,19 @@ pub use handler::{ }; pub use reference::codec; - pub use reference::object; - pub use reference::store; pub use reference::codec::BinaryDecoder; - pub use reference::codec::BinaryEncoder; pub use reference::hash::Sha512Hasher; pub use reference::object::BlobBuilder; - pub use reference::object::CommitBuilder; - pub use reference::object::TagBuilder; - pub use reference::object::TreeBuilder; - pub use reference::object::TreeEntryBuilder; pub use reference::store::MemoryRefStore; - pub use reference::store::MemoryStore; From 3f075f9a0862e984bcd002d6b3942fd641e3928d Mon Sep 17 00:00:00 2001 From: mroczect Date: Fri, 21 Aug 2026 20:33:32 +0700 Subject: [PATCH 2/2] test(root): add public API integration tests --- libvctrl/tests/public_api.rs | 215 +++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 libvctrl/tests/public_api.rs diff --git a/libvctrl/tests/public_api.rs b/libvctrl/tests/public_api.rs new file mode 100644 index 0000000..6ad4a64 --- /dev/null +++ b/libvctrl/tests/public_api.rs @@ -0,0 +1,215 @@ +use core::str::FromStr; + +use libvctrl_core as _; +use libvctrl_handler as _; +use libvctrl_sha512 as _; +use proptest as _; + +use libvctrl::{ + BinaryDecoder, BinaryEncoder, Blob, BlobBuilder, Commit, CommitBuilder, CommitMeta, Decoder, + Encoder, EntryKind, HASH_LENGTH, Hash, Hasher, MemoryRefStore, MemoryStore, ObjectStore, + RefStore, Sha512Hasher, Tag, TagBuilder, Tree, TreeBuilder, TreeEntry, TreeEntryBuilder, + UserID, VctrlError, validate_name, validate_ref_name, validate_tree_entry_name, +}; + +const fn make_hash(byte: u8) -> Result { + Hash::from_bytes(&[byte; 64]) +} + +fn make_user(name: &str, email: &str) -> Result { + UserID::new(name.to_string(), email.to_string()) +} + +#[test] +fn hash_roundtrip_through_public_api() -> Result<(), VctrlError> { + let hash = make_hash(0x42)?; + assert_eq!(hash.as_bytes().len(), HASH_LENGTH); + assert_eq!(Hash::from_str(&hash.to_string())?, hash); + Ok(()) +} + +#[test] +fn validation_functions_work() -> Result<(), VctrlError> { + validate_name("valid-name")?; + assert!(validate_name("").is_err()); + + validate_ref_name("refs/heads/main")?; + assert!(validate_ref_name("refs/heads/.hidden").is_err()); + assert!(validate_ref_name("refs/heads/foo.lock/bar").is_err()); + assert!(validate_ref_name("@").is_err()); + + validate_tree_entry_name("file.txt")?; + assert!(validate_tree_entry_name("dir/file.txt").is_err()); + + Ok(()) +} + +#[test] +fn tree_builder_and_entry_builder_work() -> Result<(), VctrlError> { + let hash = make_hash(0x11)?; + let entry = TreeEntryBuilder::new("file.txt".to_string(), EntryKind::Blob, hash).build()?; + let tree = TreeBuilder::new().entry(entry).build()?; + + let entries = tree.entries(); + assert_eq!(entries.len(), 1); + let first = entries + .first() + .ok_or_else(|| VctrlError::Other("expected entry".into()))?; + assert_eq!(first.name(), "file.txt"); + assert_eq!(first.kind(), EntryKind::Blob); + assert_eq!(*first.hash(), hash); + Ok(()) +} + +#[test] +fn blob_builder_works() -> Result<(), VctrlError> { + let data = vec![1_u8, 2, 3, 4]; + let blob = BlobBuilder::new().with_data(data.clone()).build()?; + assert_eq!(blob.data(), data.as_slice()); + Ok(()) +} + +#[test] +fn commit_and_tag_builders_work() -> Result<(), VctrlError> { + let tree_hash = make_hash(0x22)?; + let parent_hash = make_hash(0x23)?; + let author = make_user("Alice", "alice@example.com")?; + let committer = make_user("Bob", "bob@example.com")?; + let meta = CommitMeta::new(1_600_000_000, 0, Some("utf-8".into()))?; + + let commit = CommitBuilder::new() + .tree(tree_hash) + .parent(parent_hash) + .author(author) + .committer(committer) + .message("builder commit") + .meta(meta) + .build()?; + + assert_eq!(commit.tree(), &tree_hash); + assert_eq!(commit.parents(), &[parent_hash]); + assert_eq!(commit.author().name(), "Alice"); + assert_eq!(commit.committer().email(), "bob@example.com"); + assert_eq!(commit.message(), "builder commit"); + assert_eq!(commit.meta().timestamp(), 1_600_000_000); + assert_eq!(commit.meta().encoding(), Some("utf-8")); + + let tagger = make_user("Tagger", "tagger@example.com")?; + let tag = TagBuilder::new() + .name("v1.0") + .target(tree_hash) + .tagger(tagger) + .message("release") + .build()?; + + assert_eq!(tag.name(), "v1.0"); + assert_eq!(tag.target(), &tree_hash); + assert_eq!( + tag.tagger() + .ok_or_else(|| VctrlError::Other("expected tagger".into()))? + .name(), + "Tagger" + ); + assert_eq!(tag.message(), "release"); + Ok(()) +} + +#[test] +fn codec_roundtrip_through_public_api() -> Result<(), VctrlError> { + let encoder = BinaryEncoder; + let decoder = BinaryDecoder; + + // Blob + let blob = Blob::new(b"roundtrip".to_vec())?; + let mut buf = Vec::new(); + encoder.encode_blob(&blob, &mut buf)?; + let decoded_blob = decoder.decode_blob(std::io::Cursor::new(buf))?; + assert_eq!(decoded_blob.data(), blob.data()); + + // Tree + let hash = make_hash(0x33)?; + let entry = TreeEntry::new("a.txt".to_string(), EntryKind::Blob, hash)?; + let tree = Tree::new(vec![entry])?; + let mut buf = Vec::new(); + encoder.encode_tree(&tree, &mut buf)?; + let decoded_tree = decoder.decode_tree(std::io::Cursor::new(buf))?; + assert_eq!(decoded_tree.entries().len(), 1); + + // Commit + let author = make_user("Alice", "alice@example.com")?; + let committer = make_user("Bob", "bob@example.com")?; + let meta = CommitMeta::new(1_600_000_000, 0, None)?; + let commit = Commit::with_meta( + hash, + vec![hash], + author, + committer, + "commit".to_string(), + meta, + )?; + let mut buf = Vec::new(); + encoder.encode_commit(&commit, &mut buf)?; + let decoded_commit = decoder.decode_commit(std::io::Cursor::new(buf))?; + assert_eq!(decoded_commit.message(), "commit"); + + // Tag + let tagger = make_user("Tagger", "tagger@example.com")?; + let tag = Tag::with_meta( + "v1.0".to_string(), + hash, + Some(tagger), + "tag".to_string(), + CommitMeta::new(1_600_000_000, 0, None)?, + )?; + let mut buf = Vec::new(); + encoder.encode_tag(&tag, &mut buf)?; + let decoded_tag = decoder.decode_tag(std::io::Cursor::new(buf))?; + assert_eq!(decoded_tag.name(), "v1.0"); + + Ok(()) +} + +#[test] +fn hasher_public_api_works() -> Result<(), VctrlError> { + let hasher = Sha512Hasher; + let hash = hasher.hash(std::io::Cursor::new(b"test"))?; + assert_eq!(hash.as_bytes().len(), 64); + Ok(()) +} + +#[test] +fn memory_store_works() -> Result<(), VctrlError> { + let mut store = MemoryStore::new(); + let hash = make_hash(0x44)?; + let data = vec![7_u8, 8, 9]; + + store.put(&hash, &data)?; + assert!(store.exists(&hash)?); + + { + let mut reader = store.get(&hash)?; + let mut buf = Vec::new(); + let _ = std::io::Read::read_to_end(&mut reader, &mut buf)?; + assert_eq!(buf, data); + } + + store.delete(&hash)?; + assert!(!store.exists(&hash)?); + Ok(()) +} + +#[test] +fn memory_ref_store_works() -> Result<(), VctrlError> { + let mut store = MemoryRefStore::new(); + let hash = make_hash(0x55)?; + + store.set_ref("refs/heads/main", &hash)?; + assert_eq!(store.get_ref("refs/heads/main")?, hash); + + let refs: Vec = store.list_refs()?.collect::>()?; + assert_eq!(refs, vec!["refs/heads/main".to_string()]); + + store.delete_ref("refs/heads/main")?; + assert!(store.get_ref("refs/heads/main").is_err()); + Ok(()) +}