T0.10: RouteRegistry Caffeine 装载/判定(精确优先+/**通配+最严默认)与 /api/internal/routes/refresh

This commit is contained in:
chenxing
2026-09-21 00:21:28 +08:00
parent 06a02750b0
commit d4bfe8a3b4
4 changed files with 272 additions and 0 deletions
@@ -0,0 +1,112 @@
package com.yunhai.platform.gateway.route;
import com.yunhai.platform.gateway.entity.OpenApiRoute;
import com.yunhai.platform.gateway.repository.OpenApiRouteRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* RouteRegistry 五用例(T0.10 TDD):精确命中 / 前缀通配命中 / 未注册取默认(五字段全断言)/
* enabled=0 不生效 / refresh 后新增条目可见。纯单测,mock repository。
*/
class RouteRegistryTest {
private OpenApiRouteRepository routeRepo;
private RouteRegistry registry;
@BeforeEach
void setUp() {
routeRepo = Mockito.mock(OpenApiRouteRepository.class);
registry = new RouteRegistry();
ReflectionTestUtils.setField(registry, "routeRepo", routeRepo);
when(routeRepo.findByEnabledTrue()).thenReturn(new ArrayList<>(Arrays.asList(
route("usermsg", "/oapi/message/list", "*", "ANON", 30),
route("usermsg", "/oapi/message/**", "*", "SIGNED", 0))));
registry.reload();
}
private static OpenApiRoute route(String target, String pattern, String method,
String authMode, int ttl) {
OpenApiRoute r = new OpenApiRoute();
r.setTargetCode(target);
r.setPathPattern(pattern);
r.setHttpMethod(method);
r.setVisibility("INTERNAL");
r.setAuthMode(authMode);
r.setCacheTtlSeconds(ttl);
r.setCacheScope("CALLER");
r.setRateLimitQps(0);
r.setEnabled(true);
return r;
}
@Test
void 精确命中_优先于通配() {
// /oapi/message/list 同时命中精确条目(ANON)与 /oapi/message/**(SIGNED)——精确优先
RouteSpec spec = registry.resolve("usermsg", "/oapi/message/list", "GET");
assertThat(spec.authMode).isEqualTo("ANON");
assertThat(spec.cacheTtlSeconds).isEqualTo(30);
assertThat(spec.visibility).isEqualTo("INTERNAL");
assertThat(spec.cacheScope).isEqualTo("CALLER");
assertThat(spec.rateLimitQps).isZero();
}
@Test
void 前缀通配命中() {
RouteSpec spec = registry.resolve("usermsg", "/oapi/message/detail", "POST");
assertThat(spec.authMode).isEqualTo("SIGNED");
assertThat(spec.cacheTtlSeconds).isZero();
}
@Test
void 未注册路径取最严默认_五字段全断言() {
RouteSpec spec = registry.resolve("usermsg", "/oapi/unknown/path", "GET");
assertThat(spec.visibility).as("visibility").isEqualTo("INTERNAL");
assertThat(spec.authMode).as("authMode").isEqualTo("SIGNED");
assertThat(spec.cacheTtlSeconds).as("cacheTtl").isZero();
assertThat(spec.cacheScope).as("cacheScope").isEqualTo("CALLER");
assertThat(spec.rateLimitQps).as("qps").isZero();
}
@Test
void 未注册target同样取默认() {
RouteSpec spec = registry.resolve("nobody", "/oapi/anything", "GET");
assertThat(spec.authMode).isEqualTo("SIGNED");
}
@Test
void disabled条目不生效() {
// repo 契约按 enabled=true 过滤;注册表自身再滤一层(防御双保险)——disabled 条目即使漏进结果集也不参与判定
OpenApiRoute disabled = route("usermsg", "/oapi/message/disabled", "*", "ANON", 60);
disabled.setEnabled(false);
when(routeRepo.findByEnabledTrue()).thenReturn(Arrays.asList(
route("usermsg", "/oapi/message/**", "*", "SIGNED", 0), disabled));
registry.reload();
assertThat(registry.resolve("usermsg", "/oapi/message/disabled", "GET").authMode)
.as("disabled 条目不生效,走通配 SIGNED").isEqualTo("SIGNED");
}
@Test
void refresh后新增条目可见() {
assertThat(registry.resolve("notice", "/oapi/notice/list", "GET").authMode)
.as("刷新前 notice 未注册,取默认").isEqualTo("SIGNED");
List<OpenApiRoute> withNotice = new ArrayList<>(Arrays.asList(
route("usermsg", "/oapi/message/**", "*", "SIGNED", 0),
route("notice", "/oapi/notice/list", "GET", "ANON", 120)));
when(routeRepo.findByEnabledTrue()).thenReturn(withNotice);
int loaded = registry.reload();
assertThat(loaded).isEqualTo(2);
RouteSpec spec = registry.resolve("notice", "/oapi/notice/list", "GET");
assertThat(spec.authMode).as("refresh 后新增条目生效").isEqualTo("ANON");
assertThat(spec.cacheTtlSeconds).isEqualTo(120);
}
}