rust-webx-host 0.3.0

rust-webx HTTP layer: Host builder, middleware pipeline, Trie-based router, hyper integration
Documentation
//! Tests for MemoryCache implementing IDistributedCache.

use rust_webx_core::cache::{
    DistributedCacheEntryOptions, DistributedCacheExtensions, IDistributedCache,
};
use rust_webx_host::memory_cache::MemoryCache;
use serde::{Deserialize, Serialize};
use std::time::Duration;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct TestUser {
    id: u32,
    name: String,
}

#[tokio::test]
async fn set_and_get() {
    let c = MemoryCache::new();
    c.set("k", b"hello".to_vec(), None).await.unwrap();
    assert_eq!(c.get("k").await.unwrap(), Some(b"hello".to_vec()));
}

#[tokio::test]
async fn get_missing_returns_none() {
    let c = MemoryCache::new();
    assert_eq!(c.get("x").await.unwrap(), None);
}

#[tokio::test]
async fn exists_and_remove() {
    let c = MemoryCache::new();
    c.set("k", b"v".to_vec(), None).await.unwrap();
    assert!(c.exists("k").await.unwrap());
    c.remove("k").await.unwrap();
    assert!(!c.exists("k").await.unwrap());
}

#[tokio::test]
async fn clear_all() {
    let c = MemoryCache::new();
    c.set("a", b"1".to_vec(), None).await.unwrap();
    c.set("b", b"2".to_vec(), None).await.unwrap();
    c.clear().await.unwrap();
    assert_eq!(c.count().await, 0);
}

#[tokio::test]
async fn typed_get_set() {
    let c = MemoryCache::new();
    let u = TestUser {
        id: 1,
        name: "Alice".into(),
    };
    let opts = DistributedCacheEntryOptions::new();
    c.set_string("u:1", &u, &opts).await.unwrap();
    assert_eq!(c.get_string::<TestUser>("u:1").await.unwrap(), Some(u));
}

#[tokio::test]
async fn get_or_create() {
    let c = MemoryCache::new();
    let opts = DistributedCacheEntryOptions::new()
        .set_absolute_expiration_relative_to_now(Duration::from_secs(60));
    let u = c
        .get_or_create(
            "u:2",
            || async {
                TestUser {
                    id: 2,
                    name: "Bob".into(),
                }
            },
            &opts,
        )
        .await
        .unwrap();
    assert_eq!(u.name, "Bob");
    assert!(c.exists("u:2").await.unwrap());
}

#[tokio::test]
async fn sliding_extends_on_read() {
    let c = MemoryCache::new();
    let opts =
        DistributedCacheEntryOptions::new().set_sliding_expiration(Duration::from_secs(3600));
    c.set("s", b"v".to_vec(), Some(&opts)).await.unwrap();
    let _ = c.get("s").await.unwrap();
    assert!(c.exists("s").await.unwrap());
}

#[tokio::test]
async fn absolute_expiration_evicts() {
    let c = MemoryCache::new();
    let opts = DistributedCacheEntryOptions::new()
        .set_absolute_expiration_relative_to_now(Duration::from_millis(1));
    c.set("t", b"v".to_vec(), Some(&opts)).await.unwrap();
    tokio::time::sleep(Duration::from_millis(5)).await;
    assert_eq!(c.get("t").await.unwrap(), None);
}

#[tokio::test]
async fn max_entries_enforced() {
    let c = MemoryCache::new().with_max_entries(3);
    for i in 0..5 {
        c.set(&format!("k{}", i), vec![i as u8], None)
            .await
            .unwrap();
    }
    assert!(c.count().await <= 3);
}

#[tokio::test]
async fn compact_removes_expired() {
    let c = MemoryCache::new();
    let opts = DistributedCacheEntryOptions::new()
        .set_absolute_expiration_relative_to_now(Duration::from_millis(1));
    c.set("exp", b"v".to_vec(), Some(&opts)).await.unwrap();
    c.set("keep", b"v".to_vec(), None).await.unwrap();
    tokio::time::sleep(Duration::from_millis(5)).await;
    c.compact().await;
    assert!(!c.exists("exp").await.unwrap());
    assert!(c.exists("keep").await.unwrap());
}

#[tokio::test]
async fn size_limit_rejected() {
    let c = MemoryCache::new();
    let opts = DistributedCacheEntryOptions::new().set_size_limit(5);
    assert!(c
        .set("k", b"too_large".to_vec(), Some(&opts))
        .await
        .is_err());
}

#[tokio::test]
async fn builder_pattern() {
    let opts = DistributedCacheEntryOptions::new()
        .set_absolute_expiration_relative_to_now(Duration::from_secs(300))
        .set_sliding_expiration(Duration::from_secs(60))
        .set_size_limit(1024);
    assert!(opts.absolute_expiration_relative_to_now.is_some());
    assert!(opts.sliding_expiration.is_some());
    assert_eq!(opts.size_limit, 1024);
}

#[tokio::test]
async fn count_after_operations() {
    let c = MemoryCache::new();
    assert_eq!(c.count().await, 0);
    c.set("a", b"1".to_vec(), None).await.unwrap();
    c.set("b", b"2".to_vec(), None).await.unwrap();
    assert_eq!(c.count().await, 2);
    c.remove("a").await.unwrap();
    assert_eq!(c.count().await, 1);
}