diff --git a/src/test/java/com/njcn/middle/cache/integration/CacheExpireSlidingTest.java b/src/test/java/com/njcn/middle/cache/integration/CacheExpireSlidingTest.java new file mode 100644 index 0000000..b1f43e5 --- /dev/null +++ b/src/test/java/com/njcn/middle/cache/integration/CacheExpireSlidingTest.java @@ -0,0 +1,102 @@ +package com.njcn.middle.cache.integration; + +import com.njcn.middle.cache.support.CacheKeyBuilder; +import com.njcn.middle.cache.template.RedisCacheEnhanceTemplate; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.core.StringRedisTemplate; + +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * 集成测试:显式 expire 续期、滑动过期续真实值、空值标记不被续。 + * default-ttl=3600,故滑动续期会把 TTL 拉到远大于初始 60s。 + */ +@ExtendWith(RedisAvailableCondition.class) +@SpringBootTest(classes = CacheExpireSlidingTest.TestApp.class, + properties = { + "redis-cache.key-prefix=it-cache:", + "redis-cache.default-ttl-seconds=3600", + "redis-cache.null-ttl-seconds=60", + "redis-cache.jitter-ratio=0", + "redis-cache.sliding-expire-enabled=true" + }) +class CacheExpireSlidingTest { + + @Autowired + private RedisCacheEnhanceTemplate cache; + @Autowired + private StringRedisTemplate redis; + @Autowired + private CacheKeyBuilder keyBuilder; + + private long ttl(String bizKey) { + Long e = redis.getExpire(keyBuilder.build(bizKey), TimeUnit.SECONDS); + return e == null ? -1 : e; + } + + @Test + void explicitExpireExtendsTtl() { + String key = "exp:1"; + cache.set(key, new User(1L, "a"), 60); + assertTrue(ttl(key) <= 60 && ttl(key) > 0); + assertTrue(cache.expire(key, 600)); + assertTrue(ttl(key) > 100, "expire 应把 TTL 续到 600 量级"); + cache.delete(key); + } + + @Test + void expireOnMissingKeyReturnsFalse() { + String key = "exp:missing"; + cache.delete(key); + assertFalse(cache.expire(key, 600)); + } + + @Test + void slidingRenewsRealValueOnGet() { + String key = "sld:1"; + cache.set(key, new User(1L, "a"), 60); // 初始 ~60s + assertTrue(ttl(key) <= 60 && ttl(key) > 0); + cache.get(key, User.class); // 命中真实值 → 滑动续到 default 3600 + assertTrue(ttl(key) > 1000, "滑动过期应把 TTL 续到 default 量级"); + cache.delete(key); + } + + @Test + void slidingDoesNotRenewNullSentinel() { + String key = "sld:missing"; + cache.delete(key); + Supplier loader = () -> null; + assertNull(cache.getOrLoad(key, User.class, 60, loader)); // 写空标记 ttl=60 + long before = ttl(key); + assertTrue(before <= 60 && before > 0); + assertNull(cache.get(key, User.class)); // 命中空标记,不续 + assertTrue(ttl(key) <= 60, "空值标记不应被滑动续期"); + cache.delete(key); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + static class TestApp { + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + static class User { + private Long id; + private String name; + } +}