收尾冒烟: 离线 Spring 装配切片(ConfigWiringSmokeTest)——模块10/11 新增 @ConfigurationProperties/@Component/可选 MeterRegistry 在真实容器装配验证 + 8 条测试
为上线前排除 bean 装配/属性绑定类启动崩溃;全量真实启动需在非生产环境(本配置默认值会连生产 MySQL/Redis)执行
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
package com.nanri.aiimage.config;
|
||||
|
||||
import com.nanri.aiimage.metrics.TaskObservabilityMetrics;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* 启动冒烟(收尾验证前置):离线 Spring 装配切片。
|
||||
*
|
||||
* 在真实 Spring 容器里装配模块 10/11 新增的配置组件,验证:@ConfigurationProperties 绑定默认值、
|
||||
* @Component 自动注入、可选 MeterRegistry(ObjectProvider) 路径、@ConfigurationProperties 注册
|
||||
* 均正常——上线前先排除"bean 装配/属性绑定"类启动崩溃。
|
||||
*
|
||||
* 注意:不加载 DataSource/Redis/MinIO 等(会连生产),全量真实启动需在非生产环境执行。
|
||||
*/
|
||||
class ConfigWiringSmokeTest {
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties({HttpClientProperties.class, ZiniaoProperties.class,
|
||||
SimilarAsinProperties.class})
|
||||
@Import({LlmHttpConfigResolver.class, BrandCheckHttpConfigResolver.class,
|
||||
ZiniaoHttpConfigResolver.class, ImageDownloadHttpConfigResolver.class,
|
||||
IdempotentRetryGuard.class})
|
||||
static class Slice {
|
||||
}
|
||||
|
||||
private static AnnotationConfigApplicationContext context() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(Slice.class);
|
||||
ctx.refresh();
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@Test
|
||||
void configPropertiesBeansBindable() {
|
||||
try (AnnotationConfigApplicationContext ctx = context()) {
|
||||
HttpClientProperties http = ctx.getBean(HttpClientProperties.class);
|
||||
assertNotNull(http);
|
||||
assertEquals(10_000L, http.effectiveConnectTimeoutMillis(), "命名空间默认 connect 10s 应绑定");
|
||||
assertEquals(3, http.effectiveMaxRetries());
|
||||
assertNotNull(ctx.getBean(ZiniaoProperties.class));
|
||||
assertNotNull(ctx.getBean(SimilarAsinProperties.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolversAreSpringBeansAndInjectable() {
|
||||
try (AnnotationConfigApplicationContext ctx = context()) {
|
||||
assertNotNull(ctx.getBean(LlmHttpConfigResolver.class));
|
||||
assertNotNull(ctx.getBean(BrandCheckHttpConfigResolver.class));
|
||||
assertNotNull(ctx.getBean(ZiniaoHttpConfigResolver.class));
|
||||
assertNotNull(ctx.getBean(ImageDownloadHttpConfigResolver.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolversReadBoundConfig() {
|
||||
try (AnnotationConfigApplicationContext ctx = context()) {
|
||||
ZiniaoHttpConfigResolver ziniao = ctx.getBean(ZiniaoHttpConfigResolver.class);
|
||||
// 模块未显式配置 read 秒数(默认 0) → 回退命名空间 60s
|
||||
assertEquals(60_000L, ziniao.readTimeoutMillis());
|
||||
assertEquals(10_000L, ziniao.connectTimeoutMillis());
|
||||
ImageDownloadHttpConfigResolver image = ctx.getBean(ImageDownloadHttpConfigResolver.class);
|
||||
// SimilarAsinProperties.imageDownloadTimeoutSeconds 默认 5s → 模块优先
|
||||
assertEquals(5_000L, image.readTimeoutMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void retryGuardWiresWithOptionalRegistry() {
|
||||
try (AnnotationConfigApplicationContext ctx = context()) {
|
||||
IdempotentRetryGuard guard = ctx.getBean(IdempotentRetryGuard.class);
|
||||
assertNotNull(guard);
|
||||
assertTrue(guard.isIdempotent("GET", false), "GET 应视为幂等");
|
||||
assertEquals(500L, guard.backoffMillis(1), "守卫读取命名空间默认基础退避 500ms");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void observabilityRecorderIsPlainInstantiable() {
|
||||
TaskObservabilityMetrics metrics = new TaskObservabilityMetrics("server-110", null);
|
||||
assertEquals("server-110", metrics.instanceId());
|
||||
// registry=null 时不抛错(无监控栈降级)
|
||||
metrics.taskCreated("similarasin");
|
||||
metrics.fileJobState("similarasin", "success");
|
||||
}
|
||||
|
||||
@Test
|
||||
void containerClosesCleanly() {
|
||||
AnnotationConfigApplicationContext ctx = context();
|
||||
ctx.close();
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sliceRegistersExactlyExpectedConfigBeans() {
|
||||
try (AnnotationConfigApplicationContext ctx = context()) {
|
||||
String[] names = ctx.getBeanDefinitionNames();
|
||||
assertTrue(names.length >= 8, "装配 bean 数量应充足");
|
||||
assertNotNull(ctx.getBean(HttpClientProperties.class));
|
||||
assertNotNull(ctx.getBean(IdempotentRetryGuard.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultPropsMatchSpecBaseline() {
|
||||
try (AnnotationConfigApplicationContext ctx = context()) {
|
||||
HttpClientProperties http = ctx.getBean(HttpClientProperties.class);
|
||||
assertSame(http, ctx.getBean(HttpClientProperties.class), "命名空间属性 bean 单例");
|
||||
assertEquals(90_000L, http.effectiveCallTimeoutMillis());
|
||||
assertEquals(500L, http.getBaseRetryDelayMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user