use std::convert::TryInto; use std::time::Duration; use momento::{ cache::{ CollectionTtl, DecreaseTtlResponse, IncreaseTtlResponse, ItemGetTtlResponse, SetRequest, SortedSetPutElementsRequest, UpdateTtlResponse, }, MomentoErrorCode, MomentoResult, }; use momento_test_util::{ unique_cache_name, unique_key, unique_string, TestScalar, TestSortedSet, CACHE_TEST_STATE, }; mod item_get_ttl { use super::*; #[tokio::test] async fn invalid_cache_name() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let result = client.item_get_ttl(" ", "key").await.unwrap_err(); assert_eq!(result.error_code, MomentoErrorCode::InvalidArgumentError); Ok(()) } #[tokio::test] async fn nonexistent_cache() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = unique_cache_name(); let result = client.item_get_ttl(cache_name, "key").await.unwrap_err(); assert_eq!(result.error_code, MomentoErrorCode::CacheNotFoundError); Ok(()) } #[tokio::test] async fn nonexistent_key() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = CACHE_TEST_STATE.cache_name.as_str(); let key = unique_key(); let result = client.item_get_ttl(cache_name, key).await?; assert_eq!(result, ItemGetTtlResponse::Miss {}); Ok(()) } #[tokio::test] async fn get_ttl_for_a_scalar() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = CACHE_TEST_STATE.cache_name.as_str(); let item = TestScalar::new(); client .send_request( SetRequest::new(cache_name, item.key(), item.value()).ttl(Duration::from_secs(2)), ) .await?; // Should get a HIT before ttl expires let ttl: Duration = client .item_get_ttl(cache_name, item.key()) .await? .try_into() .expect("Expected an item ttl!"); assert!( ttl.as_secs() > 0, "Expected ttl to be positive, got {:?}", ttl ); // Sleep for 2 seconds tokio::time::sleep(Duration::from_secs(2)).await; // Should get a MISS after ttl expires let result = client.item_get_ttl(cache_name, item.key()).await?; assert_eq!(result, ItemGetTtlResponse::Miss {}); Ok(()) } #[tokio::test] async fn get_ttl_for_a_sorted_set() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = CACHE_TEST_STATE.cache_name.as_str(); let item = TestSortedSet::new(); // Create a sorted set that expires in 2 seconds client .send_request( SortedSetPutElementsRequest::new(cache_name, item.name(), item.value().to_vec()) .ttl(CollectionTtl::new(Some(Duration::from_secs(2)), true)), ) .await?; // Should get a HIT before ttl expires let ttl: Duration = client .item_get_ttl(cache_name, item.name()) .await? .try_into() .expect("Expected an item ttl!"); assert!( ttl.as_secs() > 0, "Expected ttl to be positive, got {:?}", ttl ); // Sleep for 2 seconds tokio::time::sleep(Duration::from_secs(2)).await; // Should get a MISS after ttl expires let result = client.item_get_ttl(cache_name, item.name()).await?; assert_eq!(result, ItemGetTtlResponse::Miss {}); Ok(()) } } mod increase_ttl { use super::*; #[tokio::test] async fn invalid_cache_name() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let result = client .increase_ttl(" ", "key", Duration::from_secs(5)) .await .unwrap_err(); assert_eq!(result.error_code, MomentoErrorCode::InvalidArgumentError); Ok(()) } #[tokio::test] async fn nonexistent_cache() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = unique_string("fake-cache"); let result = client .increase_ttl(cache_name, "key", Duration::from_secs(5)) .await .unwrap_err(); assert_eq!(result.error_code, MomentoErrorCode::CacheNotFoundError); Ok(()) } #[tokio::test] async fn nonexistent_key() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = CACHE_TEST_STATE.cache_name.as_str(); let key_uuid = unique_string("key"); let key = key_uuid.as_str(); let result = client .increase_ttl(cache_name, key, Duration::from_secs(10)) .await?; assert_eq!(result, IncreaseTtlResponse::Miss {}); Ok(()) } #[tokio::test] async fn only_increases_ttl_for_existing_key() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = CACHE_TEST_STATE.cache_name.as_str(); let item = TestScalar::new(); // Set a low TTL client .send_request( SetRequest::new(cache_name, item.key(), item.value()).ttl(Duration::from_secs(5)), ) .await?; let ttl_before: Duration = client .item_get_ttl(cache_name, item.key()) .await? .try_into() .expect("Expected an item ttl!"); assert!( ttl_before.as_secs() > 0 && ttl_before.as_secs() < 5, "Expected ttl to be <5 seconds, got {:?}", ttl_before ); // Set a higher TTL let result = client .increase_ttl(cache_name, item.key(), Duration::from_secs(20)) .await?; assert_eq!(result, IncreaseTtlResponse::Set {}); let ttl_after: Duration = client .item_get_ttl(cache_name, item.key()) .await? .try_into() .expect("Expected an item ttl!"); assert!( ttl_after.as_secs() > 15 && ttl_after.as_secs() < 20, "Expected ttl to be >15 and <20 seconds, got {:?}", ttl_after ); // Setting TTL lower than current TTL should not change the TTL let result = client .increase_ttl(cache_name, item.key(), Duration::from_secs(10)) .await?; assert_eq!(result, IncreaseTtlResponse::NotSet {}); let ttl_lower: Duration = client .item_get_ttl(cache_name, item.key()) .await? .try_into() .expect("Expected an item ttl!"); assert!( ttl_lower.as_secs() > 15 && ttl_lower.as_secs() < 20, "Expected ttl to be >15 and <20 seconds, got {:?}", ttl_lower ); Ok(()) } } mod decrease_ttl { use super::*; #[tokio::test] async fn invalid_cache_name() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let result = client .decrease_ttl(" ", "key", Duration::from_secs(5)) .await .unwrap_err(); assert_eq!(result.error_code, MomentoErrorCode::InvalidArgumentError); Ok(()) } #[tokio::test] async fn nonexistent_cache() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = unique_cache_name(); let result = client .decrease_ttl(cache_name, "key", Duration::from_secs(5)) .await .unwrap_err(); assert_eq!(result.error_code, MomentoErrorCode::CacheNotFoundError); Ok(()) } #[tokio::test] async fn nonexistent_key() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = CACHE_TEST_STATE.cache_name.as_str(); let key = unique_key(); let result = client .decrease_ttl(cache_name, key, Duration::from_secs(1)) .await?; assert_eq!(result, DecreaseTtlResponse::Miss {}); Ok(()) } #[tokio::test] async fn only_decreases_ttl_for_existing_key() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = CACHE_TEST_STATE.cache_name.as_str(); let item = TestScalar::new(); // Set a high TTL client .send_request( SetRequest::new(cache_name, item.key(), item.value()).ttl(Duration::from_secs(20)), ) .await?; let ttl_before: Duration = client .item_get_ttl(cache_name, item.key()) .await? .try_into() .expect("Expected an item ttl!"); assert!( ttl_before.as_secs() > 15 && ttl_before.as_secs() < 20, "Expected ttl to be >15 and <20 seconds, got {:?}", ttl_before ); // Set a lower TTL let result = client .decrease_ttl(cache_name, item.key(), Duration::from_secs(5)) .await?; assert_eq!(result, DecreaseTtlResponse::Set {}); let ttl_after: Duration = client .item_get_ttl(cache_name, item.key()) .await? .try_into() .expect("Expected an item ttl!"); assert!( ttl_after.as_secs() > 0 && ttl_after.as_secs() < 5, "Expected ttl to be <5 seconds, got {:?}", ttl_after ); // Setting TTL higher than current TTL should not change the TTL let result = client .decrease_ttl(cache_name, item.key(), Duration::from_secs(10)) .await?; assert_eq!(result, DecreaseTtlResponse::NotSet {}); let ttl_lower: Duration = client .item_get_ttl(cache_name, item.key()) .await? .try_into() .expect("Expected an item ttl!"); assert!( ttl_lower.as_secs() > 0 && ttl_lower.as_secs() < 5, "Expected ttl to be <5 seconds, got {:?}", ttl_lower ); Ok(()) } } mod update_ttl { use super::*; #[tokio::test] async fn invalid_cache_name() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let result = client .update_ttl(" ", "key", Duration::from_secs(5)) .await .unwrap_err(); assert_eq!(result.error_code, MomentoErrorCode::InvalidArgumentError); Ok(()) } #[tokio::test] async fn nonexistent_cache() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = unique_cache_name(); let result = client .update_ttl(cache_name, "key", Duration::from_secs(5)) .await .unwrap_err(); assert_eq!(result.error_code, MomentoErrorCode::CacheNotFoundError); Ok(()) } #[tokio::test] async fn nonexistent_key() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = CACHE_TEST_STATE.cache_name.as_str(); let key = unique_string("key"); let result = client .update_ttl(cache_name, key, Duration::from_secs(5)) .await?; assert_eq!(result, UpdateTtlResponse::Miss {}); Ok(()) } #[tokio::test] async fn overwrites_ttl_for_existing_key() -> MomentoResult<()> { let client = &CACHE_TEST_STATE.client; let cache_name = CACHE_TEST_STATE.cache_name.as_str(); let item = TestScalar::new(); client .send_request( SetRequest::new(cache_name, item.key(), item.value()).ttl(Duration::from_secs(10)), ) .await?; let ttl_before: Duration = client .item_get_ttl(cache_name, item.key()) .await? .try_into() .expect("Expected an item ttl!"); assert!( ttl_before.as_secs() > 0 && ttl_before.as_secs() < 10, "Expected ttl to be <10 seconds, got {:?}", ttl_before ); client .update_ttl(cache_name, item.key(), Duration::from_secs(20)) .await?; let ttl_after: Duration = client .item_get_ttl(cache_name, item.key()) .await? .try_into() .expect("Expected an item ttl!"); assert!( ttl_after.as_secs() > 10 && ttl_after.as_secs() < 20, "Expected ttl to be >10 and <20 seconds, got {:?}", ttl_after ); Ok(()) } }