diff --git a/src/test/java/com/ycwl/basic/integration/common/service/IntegrationFallbackServiceTest.java b/src/test/java/com/ycwl/basic/integration/common/service/IntegrationFallbackServiceTest.java new file mode 100644 index 00000000..979a21f5 --- /dev/null +++ b/src/test/java/com/ycwl/basic/integration/common/service/IntegrationFallbackServiceTest.java @@ -0,0 +1,151 @@ +package com.ycwl.basic.integration.common.service; + +import com.ycwl.basic.integration.common.config.IntegrationProperties; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.*; + +class IntegrationFallbackServiceTest { + + private IntegrationFallbackService service; + + @BeforeEach + void setUp() { + service = new IntegrationFallbackService(new IntegrationProperties()); + } + + @Test + void shouldReturnCachedValueWhenCacheExists() { + String serviceName = "zt-scenic"; + String cacheKey = "k1"; + + // 第一次调用,缓存结果 + service.executeWithFallback(serviceName, cacheKey, () -> "cached", String.class); + + // 第二次调用,应直接返回缓存,不调用远程 + AtomicInteger remoteCalls = new AtomicInteger(); + String result = service.executeWithFallback(serviceName, cacheKey, () -> { + remoteCalls.incrementAndGet(); + return "remote"; + }, String.class); + + assertEquals("cached", result); + assertEquals(0, remoteCalls.get()); + } + + @Test + void shouldCallRemoteWhenNoCacheAndCacheResult() { + String serviceName = "zt-scenic"; + String cacheKey = "k2"; + + AtomicInteger remoteCalls = new AtomicInteger(); + String result = service.executeWithFallback(serviceName, cacheKey, () -> { + remoteCalls.incrementAndGet(); + return "remote"; + }, String.class); + + assertEquals("remote", result); + assertEquals(1, remoteCalls.get()); + assertTrue(service.hasFallbackCache(serviceName, cacheKey)); + } + + @Test + void shouldThrowWhenRemoteFailsAndNoCache() { + String serviceName = "zt-scenic"; + String cacheKey = "k3"; + + RuntimeException ex = assertThrows(RuntimeException.class, () -> + service.executeWithFallback(serviceName, cacheKey, () -> { + throw new RuntimeException("boom"); + }, String.class) + ); + + assertEquals("boom", ex.getMessage()); + } + + @Test + void shouldClearCache() { + String serviceName = "zt-scenic"; + String cacheKey = "k4"; + + service.executeWithFallback(serviceName, cacheKey, () -> "value", String.class); + assertTrue(service.hasFallbackCache(serviceName, cacheKey)); + + service.clearFallbackCache(serviceName, cacheKey); + assertFalse(service.hasFallbackCache(serviceName, cacheKey)); + } + + @Test + void shouldClearAllCache() { + String serviceName = "zt-scenic"; + + service.executeWithFallback(serviceName, "key1", () -> "v1", String.class); + service.executeWithFallback(serviceName, "key2", () -> "v2", String.class); + + service.clearAllFallbackCache(serviceName); + + assertFalse(service.hasFallbackCache(serviceName, "key1")); + assertFalse(service.hasFallbackCache(serviceName, "key2")); + } + + @Test + void shouldGetStats() { + String serviceName = "zt-device"; + + service.executeWithFallback(serviceName, "d1", () -> "v1", String.class); + service.executeWithFallback(serviceName, "d2", () -> "v2", String.class); + + IntegrationFallbackService.FallbackCacheStats stats = service.getFallbackCacheStats(serviceName); + + assertEquals(serviceName, stats.getServiceName()); + assertEquals(2, stats.getTotalCacheCount()); + assertEquals(1, stats.getCacheTtlMinutes()); + } + + @Test + void shouldOnlyCallRemoteOnceWithConcurrentRequests() throws InterruptedException { + String serviceName = "zt-scenic"; + String cacheKey = "concurrent-test"; + int threadCount = 10; + + AtomicInteger remoteCalls = new AtomicInteger(); + CountDownLatch startLatch = new CountDownLatch(1); + CountDownLatch doneLatch = new CountDownLatch(threadCount); + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + + // 启动多个线程同时请求 + for (int i = 0; i < threadCount; i++) { + executor.submit(() -> { + try { + startLatch.await(); // 等待同时开始 + service.executeWithFallback(serviceName, cacheKey, () -> { + remoteCalls.incrementAndGet(); + try { + Thread.sleep(50); // 模拟远程调用耗时 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return "result"; + }, String.class); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + doneLatch.countDown(); + } + }); + } + + startLatch.countDown(); // 同时放行所有线程 + doneLatch.await(); // 等待所有线程完成 + executor.shutdown(); + + // 互斥锁生效:只有一个线程真正调用了远程 + assertEquals(1, remoteCalls.get()); + } +}