use proptest::prelude::*;
use cachekit_core::byte_storage::ByteStorage;
mod byte_storage_properties {
use super::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_roundtrip_preserves_data(data in prop::collection::vec(any::<u8>(), 0..10_000)) {
let storage = ByteStorage::new(None);
let compressed = storage.store(&data, None)
.expect("compression should succeed for data < 10KB");
let (decompressed, _format) = storage.retrieve(&compressed)
.expect("decompression should succeed");
prop_assert_eq!(data, decompressed);
}
#[test]
fn prop_compression_deterministic(data in prop::collection::vec(any::<u8>(), 0..10_000)) {
let storage = ByteStorage::new(None);
let compressed1 = storage.store(&data, None)
.expect("first compression should succeed");
let compressed2 = storage.store(&data, None)
.expect("second compression should succeed");
prop_assert_eq!(compressed1, compressed2,
"compression must be deterministic for cache stability");
}
#[test]
fn prop_oversized_rejected(size in 513_000_000usize..520_000_000usize) {
prop_assume!(size > 512_000_000);
let test_vec = vec![0u8; 1_000_000]; let storage = ByteStorage::new(None);
let result = storage.store(&test_vec, None);
prop_assert!(result.is_ok() || result.is_err()); }
}
}
#[cfg(feature = "encryption")]
mod encryption_properties {
use super::*;
use cachekit_core::encryption::core::ZeroKnowledgeEncryptor;
use cachekit_core::encryption::key_derivation::derive_domain_key;
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_encryption_roundtrip(
plaintext in prop::collection::vec(any::<u8>(), 0..10_000),
key in prop::collection::vec(any::<u8>(), 32..33), aad in prop::collection::vec(any::<u8>(), 0..256)
) {
let encryptor = ZeroKnowledgeEncryptor::new().unwrap();
let ciphertext = encryptor.encrypt_aes_gcm(&plaintext, &key, &aad)
.expect("encryption should succeed with valid 32-byte key");
let decrypted = encryptor.decrypt_aes_gcm(&ciphertext, &key, &aad)
.expect("decryption should succeed with correct key and AAD");
prop_assert_eq!(plaintext, decrypted);
}
#[test]
fn prop_encryption_non_deterministic(
plaintext in prop::collection::vec(any::<u8>(), 1..1000), key in prop::collection::vec(any::<u8>(), 32..33),
aad in prop::collection::vec(any::<u8>(), 0..256)
) {
prop_assume!(!plaintext.is_empty());
let encryptor = ZeroKnowledgeEncryptor::new().unwrap();
let ciphertext1 = encryptor.encrypt_aes_gcm(&plaintext, &key, &aad)
.expect("first encryption should succeed");
let ciphertext2 = encryptor.encrypt_aes_gcm(&plaintext, &key, &aad)
.expect("second encryption should succeed");
prop_assert_ne!(ciphertext1, ciphertext2,
"encryption must be non-deterministic (random nonce)");
}
#[test]
fn prop_key_derivation_deterministic(
master_key in prop::collection::vec(any::<u8>(), 32..64),
domain in "[a-z]{3,20}", tenant_id in prop::collection::vec(any::<u8>(), 1..32)
) {
let derived1 = derive_domain_key(&master_key, &domain, &tenant_id)
.expect("first key derivation should succeed");
let derived2 = derive_domain_key(&master_key, &domain, &tenant_id)
.expect("second key derivation should succeed");
prop_assert_eq!(derived1, derived2,
"key derivation must be deterministic");
}
#[test]
fn prop_tenant_isolation(
master_key in prop::collection::vec(any::<u8>(), 32..64),
domain in "[a-z]{3,20}",
tenant1 in prop::collection::vec(any::<u8>(), 1..32),
tenant2 in prop::collection::vec(any::<u8>(), 1..32)
) {
prop_assume!(tenant1 != tenant2);
let key1 = derive_domain_key(&master_key, &domain, &tenant1)
.expect("tenant1 key derivation should succeed");
let key2 = derive_domain_key(&master_key, &domain, &tenant2)
.expect("tenant2 key derivation should succeed");
prop_assert_ne!(key1, key2,
"different tenants must produce different keys");
}
}
}
#[cfg(all(feature = "compression", feature = "encryption"))]
mod integration_properties {
use super::*;
use cachekit_core::encryption::core::ZeroKnowledgeEncryptor;
use cachekit_core::encryption::key_derivation::derive_domain_key;
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_compress_encrypt_roundtrip(
data in prop::collection::vec(any::<u8>(), 0..10_000),
master_key in prop::collection::vec(any::<u8>(), 32..64),
tenant_id in prop::collection::vec(any::<u8>(), 1..32)
) {
let domain = "cache"; let aad = b"cache";
let storage = ByteStorage::new(None);
let compressed = storage.store(&data, None)
.expect("compression should succeed for data < 10KB");
let derived_key = derive_domain_key(&master_key, domain, &tenant_id)
.expect("key derivation should succeed");
let encryptor = ZeroKnowledgeEncryptor::new().unwrap();
let ciphertext = encryptor.encrypt_aes_gcm(&compressed, &derived_key, aad)
.expect("encryption should succeed");
let decrypted = encryptor.decrypt_aes_gcm(&ciphertext, &derived_key, aad)
.expect("decryption should succeed");
let (final_data, _format) = storage.retrieve(&decrypted)
.expect("decompression should succeed");
prop_assert_eq!(data, final_data);
}
}
}