Sfoglia il codice sorgente

fix: mutation tests jan 2 (#1486)

tsk 3 settimane fa
parent
commit
049fb5b3ab
3 ha cambiato i file con 131 aggiunte e 0 eliminazioni
  1. 8 0
      .cargo/mutants.toml
  2. 90 0
      crates/cashu/src/nuts/nut00/mod.rs
  3. 33 0
      crates/cashu/src/quote_id.rs

+ 8 - 0
.cargo/mutants.toml

@@ -57,4 +57,12 @@ exclude_re = [
     # hash_to_curve boundary check - would require finding a message that needs exactly 2^16
     # iterations which is impractical. The < vs <= mutation is not realistically testable.
     "crates/cashu/src/dhke.rs:49:.*replace < with",
+
+    # Secret::drop zeroizes memory for security. Testing this properly requires unsafe memory
+    # inspection. The security guarantee comes from using the zeroize crate correctly.
+    "crates/cashu/src/secret.rs.*impl Drop for Secret.*drop",
+
+    # serialize_to_cbor_diag is only used for CLI diagnostic output (decode_token, decode_request).
+    # Not part of any critical path - purely human-readable debugging output.
+    "crates/cashu/src/util/mod.rs.*serialize_to_cbor_diag",
 ]

+ 90 - 0
crates/cashu/src/nuts/nut00/mod.rs

@@ -1066,4 +1066,94 @@ mod tests {
 
         assert!(matches!(deserialized, Witness::P2PKWitness(_)));
     }
+
+    #[test]
+    fn test_proofs_methods_count_by_keyset() {
+        let proofs: Proofs = serde_json::from_str(
+            r#"[
+                {"id":"009a1f293253e41e","amount":2,"secret":"secret1","C":"02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"},
+                {"id":"009a1f293253e41e","amount":8,"secret":"secret2","C":"029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059"},
+                {"id":"00ad268c4d1f5826","amount":4,"secret":"secret3","C":"02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"}
+            ]"#,
+        ).unwrap();
+
+        let counts = proofs.count_by_keyset();
+        assert_eq!(counts.len(), 2);
+        assert_eq!(counts[&Id::from_str("009a1f293253e41e").unwrap()], 2);
+        assert_eq!(counts[&Id::from_str("00ad268c4d1f5826").unwrap()], 1);
+    }
+
+    #[test]
+    fn test_proofs_methods_sum_by_keyset() {
+        let proofs: Proofs = serde_json::from_str(
+            r#"[
+                {"id":"009a1f293253e41e","amount":2,"secret":"secret1","C":"02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"},
+                {"id":"009a1f293253e41e","amount":8,"secret":"secret2","C":"029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059"},
+                {"id":"00ad268c4d1f5826","amount":4,"secret":"secret3","C":"02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"}
+            ]"#,
+        ).unwrap();
+
+        let sums = proofs.sum_by_keyset();
+        assert_eq!(sums.len(), 2);
+        assert_eq!(
+            sums[&Id::from_str("009a1f293253e41e").unwrap()],
+            Amount::from(10)
+        );
+        assert_eq!(
+            sums[&Id::from_str("00ad268c4d1f5826").unwrap()],
+            Amount::from(4)
+        );
+    }
+
+    #[test]
+    fn test_proofs_methods_total_amount() {
+        let proofs: Proofs = serde_json::from_str(
+            r#"[
+                {"id":"009a1f293253e41e","amount":2,"secret":"secret1","C":"02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"},
+                {"id":"009a1f293253e41e","amount":8,"secret":"secret2","C":"029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059"},
+                {"id":"00ad268c4d1f5826","amount":4,"secret":"secret3","C":"02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"}
+            ]"#,
+        ).unwrap();
+
+        let total = proofs.total_amount().unwrap();
+        assert_eq!(total, Amount::from(14));
+    }
+
+    #[test]
+    fn test_proofs_methods_ys() {
+        let proofs: Proofs = serde_json::from_str(
+            r#"[
+                {"id":"009a1f293253e41e","amount":2,"secret":"secret1","C":"02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"},
+                {"id":"009a1f293253e41e","amount":8,"secret":"secret2","C":"029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059"}
+            ]"#,
+        ).unwrap();
+
+        let ys = proofs.ys().unwrap();
+        assert_eq!(ys.len(), 2);
+        // Each Y is hash_to_curve of the secret, verify they're different
+        assert_ne!(ys[0], ys[1]);
+    }
+
+    #[test]
+    fn test_proofs_methods_hashset() {
+        let proofs: Proofs = serde_json::from_str(
+            r#"[
+                {"id":"009a1f293253e41e","amount":2,"secret":"secret1","C":"02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"},
+                {"id":"009a1f293253e41e","amount":8,"secret":"secret2","C":"029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059"},
+                {"id":"00ad268c4d1f5826","amount":4,"secret":"secret3","C":"02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea"}
+            ]"#,
+        ).unwrap();
+
+        let proof_set: HashSet<Proof> = proofs.into_iter().collect();
+
+        // Test HashSet implementation of ProofsMethods
+        let counts = proof_set.count_by_keyset();
+        assert_eq!(counts.len(), 2);
+
+        let sums = proof_set.sum_by_keyset();
+        assert_eq!(sums.len(), 2);
+        // Total should be 14 (2 + 8 + 4)
+        let total: u64 = sums.values().map(|a| u64::from(*a)).sum();
+        assert_eq!(total, 14);
+    }
 }

+ 33 - 0
crates/cashu/src/quote_id.rs

@@ -98,3 +98,36 @@ impl<'de> Deserialize<'de> for QuoteId {
         )))
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_quote_id_display_uuid() {
+        // Test UUID display - should be hyphenated format
+        let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
+        let quote_id = QuoteId::UUID(uuid);
+        let displayed = quote_id.to_string();
+        assert_eq!(displayed, "550e8400-e29b-41d4-a716-446655440000");
+        assert!(!displayed.is_empty());
+
+        // Verify roundtrip works for UUID
+        let parsed: QuoteId = displayed.parse().unwrap();
+        assert_eq!(quote_id, parsed);
+    }
+
+    #[test]
+    fn test_quote_id_display_base64() {
+        // Test BASE64 display - should output the string as-is
+        let base64_str = "SGVsbG8gV29ybGQh"; // "Hello World!" with proper padding
+        let base64_id = QuoteId::BASE64(base64_str.to_string());
+        let displayed = base64_id.to_string();
+        assert_eq!(displayed, base64_str);
+        assert!(!displayed.is_empty());
+
+        // Verify roundtrip works for base64
+        let parsed: QuoteId = displayed.parse().unwrap();
+        assert_eq!(base64_id, parsed);
+    }
+}