#[cfg(feature = "cache")]
mod cache_tests {
use gouqi::cache::{
Cache, CacheStats, MemoryCache, RuntimeCacheConfig, RuntimeCacheStrategy,
generate_cache_key, jira_cache_key,
};
use std::collections::HashMap;
use std::thread;
use std::time::Duration;
#[test]
fn test_memory_cache_new() {
let cache = MemoryCache::new(Duration::from_secs(60));
let stats = cache.stats();
assert_eq!(stats.total_entries, 0);
assert_eq!(stats.active_entries, 0);
assert_eq!(cache.default_ttl(), Duration::from_secs(60));
}
#[test]
fn test_memory_cache_with_capacity() {
let cache = MemoryCache::with_capacity(10, Duration::from_secs(30));
let stats = cache.stats();
assert_eq!(stats.max_capacity, 10);
assert_eq!(cache.default_ttl(), Duration::from_secs(30));
}
#[test]
fn test_cache_set_and_get() {
let cache = MemoryCache::new(Duration::from_secs(60));
let key = "test_key";
let value = b"test_value".to_vec();
cache.set(key, value.clone(), Duration::from_secs(60));
let retrieved = cache.get(key);
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap(), value);
}
#[test]
fn test_cache_expiration() {
let cache = MemoryCache::new(Duration::from_secs(60));
let key = "expiring_key";
let value = b"expiring_value".to_vec();
cache.set(key, value, Duration::from_millis(10));
assert!(cache.get(key).is_some());
thread::sleep(Duration::from_millis(20));
assert!(cache.get(key).is_none());
}
#[test]
fn test_cache_delete() {
let cache = MemoryCache::new(Duration::from_secs(60));
let key = "delete_key";
let value = b"delete_value".to_vec();
cache.set(key, value, Duration::from_secs(60));
assert!(cache.get(key).is_some());
cache.delete(key);
assert!(cache.get(key).is_none());
}
#[test]
fn test_cache_clear() {
let cache = MemoryCache::new(Duration::from_secs(60));
cache.set("key1", b"value1".to_vec(), Duration::from_secs(60));
cache.set("key2", b"value2".to_vec(), Duration::from_secs(60));
cache.set("key3", b"value3".to_vec(), Duration::from_secs(60));
let stats = cache.stats();
assert_eq!(stats.total_entries, 3);
cache.clear();
let stats_after = cache.stats();
assert_eq!(stats_after.total_entries, 0);
}
#[test]
fn test_cache_stats() {
let cache = MemoryCache::new(Duration::from_secs(60));
cache.set("key1", b"short".to_vec(), Duration::from_secs(60));
cache.set("key2", b"longer_value".to_vec(), Duration::from_millis(10));
let stats = cache.stats();
assert_eq!(stats.total_entries, 2);
assert!(stats.total_size_bytes > 0);
thread::sleep(Duration::from_millis(20));
let stats_after = cache.stats();
assert_eq!(stats_after.total_entries, 2); assert_eq!(stats_after.active_entries, 1); assert_eq!(stats_after.expired_entries, 1); }
#[test]
fn test_cache_cleanup_expired() {
let cache = MemoryCache::new(Duration::from_secs(60));
cache.set("active", b"value1".to_vec(), Duration::from_secs(60));
cache.set("expiring", b"value2".to_vec(), Duration::from_millis(10));
thread::sleep(Duration::from_millis(20));
let stats_before = cache.stats();
assert_eq!(stats_before.total_entries, 2);
cache.cleanup_expired();
let stats_after = cache.stats();
assert_eq!(stats_after.total_entries, 1);
assert_eq!(stats_after.active_entries, 1);
}
#[test]
fn test_cache_lru_eviction() {
let cache = MemoryCache::with_capacity(3, Duration::from_secs(60));
cache.set("key1", b"value1".to_vec(), Duration::from_secs(60));
thread::sleep(Duration::from_millis(10));
cache.set("key2", b"value2".to_vec(), Duration::from_secs(60));
thread::sleep(Duration::from_millis(10));
cache.set("key3", b"value3".to_vec(), Duration::from_secs(60));
let stats = cache.stats();
assert_eq!(stats.total_entries, 3);
cache.set("key4", b"value4".to_vec(), Duration::from_secs(60));
assert!(cache.get("key1").is_none());
assert!(cache.get("key2").is_some());
assert!(cache.get("key3").is_some());
assert!(cache.get("key4").is_some());
let stats_after = cache.stats();
assert_eq!(stats_after.total_entries, 3);
}
#[test]
fn test_cache_stats_utilization() {
let stats = CacheStats {
total_entries: 5,
active_entries: 8,
expired_entries: 2,
total_size_bytes: 1000,
max_capacity: 10,
};
assert_eq!(stats.utilization_percent(), 80.0);
}
#[test]
fn test_cache_stats_avg_entry_size() {
let stats = CacheStats {
total_entries: 5,
active_entries: 4,
expired_entries: 1,
total_size_bytes: 400,
max_capacity: 10,
};
assert_eq!(stats.avg_entry_size_bytes(), 100.0);
}
#[test]
fn test_cache_stats_empty() {
let stats = CacheStats::empty();
assert_eq!(stats.total_entries, 0);
assert_eq!(stats.active_entries, 0);
assert_eq!(stats.expired_entries, 0);
assert_eq!(stats.total_size_bytes, 0);
assert_eq!(stats.max_capacity, 0);
assert_eq!(stats.utilization_percent(), 0.0);
assert_eq!(stats.avg_entry_size_bytes(), 0.0);
}
#[test]
fn test_generate_cache_key() {
let key1 = generate_cache_key("/rest/api/2/issue/TEST-1", "");
let key2 = generate_cache_key("/rest/api/2/issue/TEST-1", "expand=changelog");
let key3 = generate_cache_key("/rest/api/2/issue/TEST-2", "");
let key1_again = generate_cache_key("/rest/api/2/issue/TEST-1", "");
assert_eq!(key1, key1_again);
assert_ne!(key1, key2);
assert_ne!(key1, key3);
assert!(key1.contains("_rest_api_2_issue_TEST-1"));
}
#[test]
fn test_jira_cache_key() {
let key1 = jira_cache_key("issues", "TEST-1", "");
let key2 = jira_cache_key("issues", "TEST-1", "expand=changelog");
let key3 = jira_cache_key("issues", "TEST-2", "");
let key4 = jira_cache_key("issues", "", "jql=project=TEST");
let key1_again = jira_cache_key("issues", "TEST-1", "");
assert_eq!(key1, key1_again);
assert_ne!(key1, key2);
assert_ne!(key1, key3);
assert!(key4.contains("issues"));
}
#[test]
fn test_runtime_cache_config_default() {
let config = RuntimeCacheConfig::default();
assert!(config.enabled);
assert_eq!(config.default_ttl, Duration::from_secs(300));
assert_eq!(config.max_entries, 1000);
assert!(config.strategies.contains_key("issues"));
assert!(config.strategies.contains_key("projects"));
assert!(config.strategies.contains_key("users"));
assert!(config.strategies.contains_key("search"));
}
#[test]
fn test_runtime_cache_config_strategy_for_endpoint() {
let config = RuntimeCacheConfig::default();
let issues_strategy = config.strategy_for_endpoint("/rest/api/2/issues/TEST-1");
assert_eq!(issues_strategy.ttl, Duration::from_secs(300));
let projects_strategy = config.strategy_for_endpoint("/rest/api/2/projects");
assert_eq!(projects_strategy.ttl, Duration::from_secs(3600));
let users_strategy = config.strategy_for_endpoint("/rest/api/2/users/john");
assert_eq!(users_strategy.ttl, Duration::from_secs(1800));
let unknown_strategy = config.strategy_for_endpoint("/rest/api/2/unknown");
assert_eq!(unknown_strategy.ttl, config.default_ttl);
}
#[test]
fn test_runtime_cache_config_should_cache_endpoint() {
let config = RuntimeCacheConfig::default();
assert!(config.should_cache_endpoint("/rest/api/2/issues/TEST-1"));
assert!(config.should_cache_endpoint("/rest/api/2/projects"));
assert!(!config.should_cache_endpoint("/rest/api/2/search"));
}
#[test]
fn test_runtime_cache_config_disabled() {
let config = RuntimeCacheConfig {
enabled: false,
..Default::default()
};
assert!(!config.should_cache_endpoint("/rest/api/2/issues/TEST-1"));
assert!(!config.should_cache_endpoint("/rest/api/2/projects"));
}
#[test]
fn test_runtime_cache_strategy() {
let strategy = RuntimeCacheStrategy {
ttl: Duration::from_secs(600),
cache_errors: true,
use_etag: false,
};
assert_eq!(strategy.ttl, Duration::from_secs(600));
assert!(strategy.cache_errors);
assert!(!strategy.use_etag);
}
#[test]
fn test_runtime_cache_config_custom_strategies() {
let mut strategies = HashMap::new();
strategies.insert(
"custom".to_string(),
RuntimeCacheStrategy {
ttl: Duration::from_secs(120),
cache_errors: false,
use_etag: true,
},
);
let config = RuntimeCacheConfig {
enabled: true,
default_ttl: Duration::from_secs(60),
max_entries: 500,
strategies,
};
let custom_strategy = config.strategy_for_endpoint("/rest/api/2/custom");
assert_eq!(custom_strategy.ttl, Duration::from_secs(120));
}
}