diff --git a/src/manifest.rs b/src/manifest.rs index 6141a40d..ec9698e0 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -1349,6 +1349,52 @@ mod tests { assert!(serialized.contains("[package]")); assert!(serialized.contains("test")); } + + #[test] + fn packages_manifest_serialization_is_consistent_regardless_of_empty_dependencies_section() { + // A manifest with an empty [dependencies] section should serialize + // identically to a manifest without a [dependencies] section. + // This is important for reproducible package hashes. + use crate::lock::DigestAlgorithm; + + let toml_with_empty_deps = r#" + edition = "0.12" + + [package] + type = "lib" + name = "test" + version = "1.0.0" + + [dependencies] + "#; + + let toml_without_deps = r#" + edition = "0.12" + + [package] + type = "lib" + name = "test" + version = "1.0.0" + "#; + + let manifest_with = PackagesManifest::from_str(toml_with_empty_deps).expect("should parse"); + let manifest_without = PackagesManifest::from_str(toml_without_deps).expect("should parse"); + + let serialized_with: String = manifest_with.try_into().expect("should serialize"); + let serialized_without: String = manifest_without.try_into().expect("should serialize"); + + let hash_with = DigestAlgorithm::SHA256.digest(serialized_with.as_bytes()); + let hash_without = DigestAlgorithm::SHA256.digest(serialized_without.as_bytes()); + + assert_eq!( + hash_with, hash_without, + "Package hashes must be identical for reproducible builds.\n\ + Hash with [dependencies]: {}\n\ + Hash without [dependencies]: {}\n\n\ + With [dependencies]:\n{}\n\nWithout [dependencies]:\n{}", + hash_with, hash_without, serialized_with, serialized_without + ); + } } // ===== WorkspaceManifest Tests =====