fncache 0.1.2

A zero-boilerplate Rust library for function-level caching with pluggable backends
Documentation
#[cfg(all(test, feature = "test-utils", feature = "memory"))]
mod tests {
    use super::*;
    use crate::{
        backends::memory::MemoryBackend,
        invalidation::{AsyncCacheInvalidation, CacheInvalidation, InvalidationCache, Tag},
    };
    use std::time::Duration;
    use tokio::runtime::Runtime;

    #[test]
    fn test_register_with_tags() {
        let cache = InvalidationCache::new(MemoryBackend::new());
        let key = "user:123".to_string();
        let tags = vec![Tag::new("user"), Tag::new("profile")];

        cache.register_key_with_tags(&key, tags.clone());

        let tag_map = cache.get_tag_map();
        assert!(tag_map.contains_key(&Tag::new("user")));
        assert!(tag_map.contains_key(&Tag::new("profile")));

        let user_keys = tag_map.get(&Tag::new("user")).unwrap();
        assert!(user_keys.contains(&key));

        let profile_keys = tag_map.get(&Tag::new("profile")).unwrap();
        assert!(profile_keys.contains(&key));
    }

    #[test]
    fn test_register_with_prefixes() {
        let cache = InvalidationCache::new(MemoryBackend::new());
        let key = "users:123:profile".to_string();

        cache.register_key_with_tags(&key, Vec::<Tag>::new());

        let prefix_map = cache.get_prefix_map();
        assert!(prefix_map.contains_key(&"users".to_string()));
        assert!(prefix_map.contains_key(&"users:123".to_string()));

        let users_keys = prefix_map.get(&"users".to_string()).unwrap();
        assert!(users_keys.contains(&key));

        let user_123_keys = prefix_map.get(&"users:123".to_string()).unwrap();
        assert!(user_123_keys.contains(&key));
    }

    #[tokio::test]
    async fn test_invalidate_tag() {
        let cache = InvalidationCache::new(MemoryBackend::new());
        let key1 = "user:123".to_string();
        let key2 = "user:456".to_string();

        cache
            .set_with_tags(key1.clone(), "value1", None, vec![Tag::new("user")])
            .await
            .unwrap();

        cache
            .set_with_tags(
                key2.clone(),
                "value2",
                None,
                vec![Tag::new("user"), Tag::new("vip")],
            )
            .await
            .unwrap();

        assert_eq!(
            cache.get::<String>(&key1).await.unwrap(),
            Some("value1".to_string())
        );
        assert_eq!(
            cache.get::<String>(&key2).await.unwrap(),
            Some("value2".to_string())
        );

        AsyncCacheInvalidation::invalidate_tag(&cache, &Tag::new("user"))
            .await
            .unwrap();
        assert_eq!(cache.get::<String>(&key1).await.unwrap(), None);
        assert_eq!(cache.get::<String>(&key2).await.unwrap(), None);
    }

    #[tokio::test]
    async fn test_invalidate_prefix() {
        let cache = InvalidationCache::new(MemoryBackend::new());
        let key1 = "users:123:profile".to_string();
        let key2 = "users:123:settings".to_string();
        let key3 = "users:456:profile".to_string();

        cache
            .set_with_tags(key1.clone(), "profile1", None, Vec::<Tag>::new())
            .await
            .unwrap();
        cache
            .set_with_tags(key2.clone(), "settings1", None, Vec::<Tag>::new())
            .await
            .unwrap();
        cache
            .set_with_tags(key3.clone(), "profile2", None, Vec::<Tag>::new())
            .await
            .unwrap();

        assert_eq!(
            cache.get::<String>(&key1).await.unwrap(),
            Some("profile1".to_string())
        );
        assert_eq!(
            cache.get::<String>(&key2).await.unwrap(),
            Some("settings1".to_string())
        );
        assert_eq!(
            cache.get::<String>(&key3).await.unwrap(),
            Some("profile2".to_string())
        );

        AsyncCacheInvalidation::invalidate_prefix(&cache, "users:123")
            .await
            .unwrap();
        assert_eq!(cache.get::<String>(&key1).await.unwrap(), None);
        assert_eq!(cache.get::<String>(&key2).await.unwrap(), None);
        assert_eq!(
            cache.get::<String>(&key3).await.unwrap(),
            Some("profile2".to_string())
        );
    }

    #[test]
    fn test_sync_invalidate_tag() {
        let cache = InvalidationCache::new(MemoryBackend::new());
        let key1 = "user:123".to_string();
        let key2 = "user:456".to_string();
        let rt = Runtime::new().unwrap();

        rt.block_on(async {
            cache
                .set_with_tags(key1.clone(), "value1", None, vec![Tag::new("user")])
                .await
                .unwrap();

            cache
                .set_with_tags(
                    key2.clone(),
                    "value2",
                    None,
                    vec![Tag::new("user"), Tag::new("vip")],
                )
                .await
                .unwrap();

            assert_eq!(
                cache.get::<String>(&key1).await.unwrap(),
                Some("value1".to_string())
            );
            assert_eq!(
                cache.get::<String>(&key2).await.unwrap(),
                Some("value2".to_string())
            );
        });

        CacheInvalidation::invalidate_tag(&cache, &Tag::new("user")).unwrap();

        rt.block_on(async {
            assert_eq!(cache.get::<String>(&key1).await.unwrap(), None);
            assert_eq!(cache.get::<String>(&key2).await.unwrap(), None);
        });
    }
}