#![allow(clippy::unwrap_used)] #![allow(missing_docs)]
mod common;
use fraiseql_core::{
cache::{CacheConfig, CachedDatabaseAdapter, QueryResultCache},
db::DatabaseAdapter,
};
#[tokio::test]
async fn cache_hit_avoids_second_real_query() {
let adapter = common::testcontainer::get_test_adapter().await;
let cache = QueryResultCache::new(CacheConfig::enabled());
let cached = CachedDatabaseAdapter::new(adapter, cache, "v1".to_string());
let r1 = cached.execute_where_query("test.v_user", None, None, None, None).await.unwrap();
let r2 = cached.execute_where_query("test.v_user", None, None, None, None).await.unwrap();
assert_eq!(r1.len(), r2.len());
let metrics = cached.cache().metrics().unwrap();
assert_eq!(metrics.hits, 1, "second call should be a cache hit");
assert_eq!(metrics.misses, 1, "first call should be a cache miss");
}
#[tokio::test]
async fn cache_invalidation_forces_refetch() {
let adapter = common::testcontainer::get_test_adapter().await;
let cache = QueryResultCache::new(CacheConfig::enabled());
let cached = CachedDatabaseAdapter::new(adapter, cache, "v1".to_string());
let _ = cached.execute_where_query("test.v_user", None, None, None, None).await.unwrap();
let evicted = cached.invalidate_views(&["test.v_user".to_string()]).unwrap();
assert!(evicted > 0, "should have evicted at least one entry");
let _ = cached.execute_where_query("test.v_user", None, None, None, None).await.unwrap();
let metrics = cached.cache().metrics().unwrap();
assert_eq!(metrics.misses, 2, "post-invalidation call should miss");
assert_eq!(metrics.hits, 0, "no hits expected after invalidation");
}
#[tokio::test]
async fn different_views_cache_independently() {
let adapter = common::testcontainer::get_test_adapter().await;
let cache = QueryResultCache::new(CacheConfig::enabled());
let cached = CachedDatabaseAdapter::new(adapter, cache, "v1".to_string());
let _ = cached.execute_where_query("test.v_user", None, None, None, None).await.unwrap();
let _ = cached
.execute_where_query("test.v_project", None, None, None, None)
.await
.unwrap();
let _ = cached.execute_where_query("test.v_user", None, None, None, None).await.unwrap();
let _ = cached
.execute_where_query("test.v_project", None, None, None, None)
.await
.unwrap();
let metrics = cached.cache().metrics().unwrap();
assert_eq!(metrics.misses, 2);
assert_eq!(metrics.hits, 2);
}
#[tokio::test]
async fn different_query_params_cache_independently() {
let adapter = common::testcontainer::get_test_adapter().await;
let cache = QueryResultCache::new(CacheConfig::enabled());
let cached = CachedDatabaseAdapter::new(adapter, cache, "v1".to_string());
let r1 = cached
.execute_where_query("test.v_project", None, Some(1), None, None)
.await
.unwrap();
let r2 = cached
.execute_where_query("test.v_project", None, Some(2), None, None)
.await
.unwrap();
let metrics = cached.cache().metrics().unwrap();
assert_eq!(metrics.misses, 2, "different limits should produce different cache keys");
assert_eq!(metrics.hits, 0);
assert!(r1.len() <= 1);
assert!(r2.len() <= 2);
}
#[tokio::test]
async fn disabled_cache_always_hits_database() {
let adapter = common::testcontainer::get_test_adapter().await;
let cache = QueryResultCache::new(CacheConfig::disabled());
let cached = CachedDatabaseAdapter::new(adapter, cache, "v1".to_string());
let r1 = cached.execute_where_query("test.v_user", None, None, None, None).await.unwrap();
let r2 = cached.execute_where_query("test.v_user", None, None, None, None).await.unwrap();
assert_eq!(r1.len(), r2.len());
let metrics = cached.cache().metrics().unwrap();
assert_eq!(metrics.hits, 0);
}