#[cfg(test)]
mod caching_tests {
use keeper_secrets_manager_core::caching::{
cache_exists, clear_cache, get_cache_file_path, get_cached_data, save_cache,
};
use serial_test::serial;
use std::env;
use std::fs;
use std::path::PathBuf;
use std::sync::atomic::{AtomicU32, Ordering};
static TEST_COUNTER: AtomicU32 = AtomicU32::new(0);
fn get_test_cache_dir() -> PathBuf {
let counter = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
let test_id = format!("{}_{}", std::process::id(), counter);
let temp_dir = env::temp_dir();
temp_dir.join(format!("ksm_test_cache_{}", test_id))
}
fn setup_test_cache() -> (String, PathBuf) {
let test_dir = get_test_cache_dir();
fs::create_dir_all(&test_dir).unwrap();
let cache_dir_str = test_dir.to_str().unwrap().to_string();
(cache_dir_str, test_dir)
}
fn cleanup_test_cache(test_dir: PathBuf) {
if test_dir.exists() {
fs::remove_dir_all(&test_dir).ok();
}
}
#[test]
#[serial]
fn test_save_and_get_cache() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let test_data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let save_result = save_cache(&test_data);
assert!(save_result.is_ok());
let cached_data = get_cached_data();
assert!(cached_data.is_some());
assert_eq!(cached_data.unwrap(), test_data);
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_cache_exists() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
assert!(!cache_exists());
let test_data = vec![1, 2, 3];
save_cache(&test_data).unwrap();
assert!(cache_exists());
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_clear_cache() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let test_data = vec![1, 2, 3, 4, 5];
save_cache(&test_data).unwrap();
assert!(cache_exists());
let clear_result = clear_cache();
assert!(clear_result.is_ok());
assert!(!cache_exists());
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_clear_cache_when_not_exists() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
assert!(!cache_exists());
let clear_result = clear_cache();
assert!(clear_result.is_ok());
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_get_cached_data_not_exists() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let cached_data = get_cached_data();
assert!(cached_data.is_none());
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_save_empty_cache() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let empty_data = vec![];
let save_result = save_cache(&empty_data);
assert!(save_result.is_ok());
let cached_data = get_cached_data();
assert!(cached_data.is_some());
assert_eq!(cached_data.unwrap().len(), 0);
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_save_large_cache() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let large_data = vec![0u8; 1024 * 1024];
let save_result = save_cache(&large_data);
assert!(save_result.is_ok());
let cached_data = get_cached_data();
assert!(cached_data.is_some());
assert_eq!(cached_data.unwrap().len(), 1024 * 1024);
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_cache_overwrite() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let initial_data = vec![1, 2, 3];
save_cache(&initial_data).unwrap();
let cached = get_cached_data().unwrap();
assert_eq!(cached, initial_data);
let new_data = vec![4, 5, 6, 7, 8];
save_cache(&new_data).unwrap();
let cached = get_cached_data().unwrap();
assert_eq!(cached, new_data);
assert_ne!(cached, initial_data);
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_cache_file_path_custom_dir() {
let custom_dir = "/custom/cache/dir";
env::set_var("KSM_CACHE_DIR", custom_dir);
let cache_path = get_cache_file_path();
assert!(cache_path.to_str().unwrap().contains(custom_dir));
assert!(cache_path.to_str().unwrap().contains("ksm_cache.bin"));
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_cache_file_path_default_dir() {
env::remove_var("KSM_CACHE_DIR");
let cache_path = get_cache_file_path();
assert!(cache_path.to_str().unwrap().ends_with("ksm_cache.bin"));
let expected_path = PathBuf::from(".").join("ksm_cache.bin");
assert_eq!(cache_path, expected_path);
}
#[test]
#[serial]
fn test_cache_binary_data_roundtrip() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let binary_data = vec![
0x00, 0xFF, 0x42, 0x13, 0x37, 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE,
];
save_cache(&binary_data).unwrap();
let cached = get_cached_data().unwrap();
assert_eq!(cached, binary_data);
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_cache_transmission_key_format() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let transmission_key = vec![0u8; 32]; let encrypted_response = vec![1, 2, 3, 4, 5, 6, 7, 8];
let mut cache_data = transmission_key.clone();
cache_data.extend_from_slice(&encrypted_response);
save_cache(&cache_data).unwrap();
let cached = get_cached_data().unwrap();
assert_eq!(cached.len(), 32 + 8); assert_eq!(&cached[0..32], &transmission_key[..]);
assert_eq!(&cached[32..], &encrypted_response[..]);
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_cache_multiple_saves() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
for i in 0..10 {
let data = vec![i as u8; 100];
let save_result = save_cache(&data);
assert!(save_result.is_ok());
let cached = get_cached_data().unwrap();
assert_eq!(cached, data);
}
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_cache_minimum_size() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let minimum_data = vec![0u8; 32];
save_cache(&minimum_data).unwrap();
let cached = get_cached_data().unwrap();
assert_eq!(cached.len(), 32);
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_cache_realistic_size() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let transmission_key = vec![0u8; 32];
let encrypted_response = vec![1u8; 37_000];
let mut cache_data = transmission_key;
cache_data.extend_from_slice(&encrypted_response);
save_cache(&cache_data).unwrap();
let cached = get_cached_data().unwrap();
assert_eq!(cached.len(), 32 + 37_000);
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_cache_multiple_clears() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let test_data = vec![1, 2, 3, 4, 5];
save_cache(&test_data).unwrap();
assert!(cache_exists());
for _ in 0..5 {
let clear_result = clear_cache();
assert!(clear_result.is_ok());
assert!(!cache_exists());
}
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_cache_file_path_format() {
env::set_var("KSM_CACHE_DIR", "/tmp/test");
let path = get_cache_file_path();
let path_str = path.to_str().unwrap();
assert!(path_str.contains("ksm_cache.bin"));
assert!(path_str.contains("/tmp/test"));
env::remove_var("KSM_CACHE_DIR");
}
#[test]
#[serial]
fn test_caching_module_exists() {
let (cache_dir, test_dir) = setup_test_cache();
env::set_var("KSM_CACHE_DIR", &cache_dir);
let cache_path = get_cache_file_path();
assert!(cache_path.to_str().unwrap().contains("ksm_cache.bin"));
let _ = clear_cache();
assert!(!cache_exists(), "Cache should not exist after clearing");
let test_data = b"test cache data for validation";
save_cache(test_data).expect("Failed to save cache data");
assert!(
cache_exists(),
"Cache should exist after saving. Path: {:?}",
cache_path
);
let loaded = get_cached_data().expect("Failed to retrieve cached data");
assert_eq!(loaded, test_data, "Cached data should match original");
clear_cache().expect("Failed to clear cache");
cleanup_test_cache(test_dir);
env::remove_var("KSM_CACHE_DIR");
}
#[test]
fn test_make_caching_post_function_runs_under_spawn_blocking() {
use keeper_secrets_manager_core::caching::make_caching_post_function;
use keeper_secrets_manager_core::dto::{EncryptedPayload, TransmissionKey};
use p256::ecdsa::{signature::Signer, SigningKey};
let signing_key = SigningKey::from_slice(&[42u8; 32]).expect("valid test scalar");
let raw_sig: p256::ecdsa::Signature = signing_key.sign(b"ksm-931-regression-test");
let der_sig: ecdsa::der::Signature<p256::NistP256> = raw_sig.into();
let tk = TransmissionKey::new("10".to_string(), vec![0u8; 32], vec![0u8; 32]);
let ep = EncryptedPayload::new(vec![0u8; 16], der_sig);
let client = reqwest::blocking::Client::builder()
.build()
.expect("build client outside runtime");
let post_fn = make_caching_post_function(client);
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("build tokio runtime");
let result = rt.block_on(async {
tokio::task::spawn_blocking(move || {
post_fn("http://127.0.0.1:1/test".to_string(), tk, ep)
})
.await
});
assert!(
result.is_ok(),
"spawn_blocking panicked (nested-runtime bug still present): {:?}",
result
);
let inner = result.unwrap();
assert!(
inner.is_err(),
"expected network error against 127.0.0.1:1, got Ok"
);
}
}