From 08b1735b0e6bf860b55534924397696f8b31b0ec Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Sun, 19 Apr 2026 15:47:16 -0400 Subject: [PATCH] test(core): integration tests for attachments (round-trip, AID, caps) Also derives Debug on EncryptedAttachment (required by the test's panic arm). Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/idfoto-core/src/attachment.rs | 1 + crates/idfoto-core/tests/attachments.rs | 52 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 crates/idfoto-core/tests/attachments.rs diff --git a/crates/idfoto-core/src/attachment.rs b/crates/idfoto-core/src/attachment.rs index d0da491..6e6c9b2 100644 --- a/crates/idfoto-core/src/attachment.rs +++ b/crates/idfoto-core/src/attachment.rs @@ -46,6 +46,7 @@ use crate::crypto::{decrypt, encrypt}; use crate::error::{IdfotoError, Result}; /// Encrypted attachment with the AID derived from plaintext content. +#[derive(Debug)] pub struct EncryptedAttachment { pub id: AttachmentId, pub bytes: Vec, diff --git a/crates/idfoto-core/tests/attachments.rs b/crates/idfoto-core/tests/attachments.rs new file mode 100644 index 0000000..8ecf188 --- /dev/null +++ b/crates/idfoto-core/tests/attachments.rs @@ -0,0 +1,52 @@ +//! Attachment encrypt/decrypt + content-addressed AID + cap enforcement. + +use idfoto_core::{ + AttachmentId, IdfotoError, + crypto::KdfParams, + decrypt_attachment, derive_master_key, encrypt_attachment, +}; +use zeroize::Zeroizing; + +fn fast_params() -> KdfParams { KdfParams { argon2_m: 256, argon2_t: 1, argon2_p: 1 } } + +fn make_key() -> Zeroizing<[u8; 32]> { + derive_master_key(b"x", &[0u8; 32], &[0u8; 32], &fast_params()).unwrap() +} + +#[test] +fn attachment_round_trip_5kb() { + let plaintext: Vec = (0..5000u32).map(|i| (i & 0xff) as u8).collect(); + let key = make_key(); + let enc = encrypt_attachment(&plaintext, &key, 10 * 1024 * 1024).unwrap(); + assert_eq!(enc.id, AttachmentId::from_plaintext(&plaintext)); + + let dec = decrypt_attachment(&enc.bytes, &key).unwrap(); + assert_eq!(&*dec, &plaintext); +} + +#[test] +fn identical_plaintexts_yield_identical_aids() { + let plaintext = b"hello world"; + let key = make_key(); + let a = encrypt_attachment(plaintext, &key, 1024).unwrap(); + let b = encrypt_attachment(plaintext, &key, 1024).unwrap(); + assert_eq!(a.id, b.id); + // (Bytes will differ because nonce is random per-encryption — that's expected.) +} + +#[test] +fn cap_enforcement_at_exact_max() { + let plaintext = vec![0u8; 1024]; + let key = make_key(); + // Exactly at max — should pass + let _ = encrypt_attachment(&plaintext, &key, 1024).unwrap(); + // One byte over — should fail + let err = encrypt_attachment(&plaintext, &key, 1023); + match err { + Err(IdfotoError::AttachmentTooLarge { size, max }) => { + assert_eq!(size, 1024); + assert_eq!(max, 1023); + } + other => panic!("expected AttachmentTooLarge, got {other:?}"), + } +}