test(message): 切片 wiring 测试改用真实 autoconfig 顺序
MqSliceWiringTest 从 withUserConfiguration 手动排序改为 AutoConfigurations.of (等价真实 boot 的 AutoConfigurationSorter),并断言 MqTemplate / MqListenerRegistry 在真实顺序下装配,防止 core 装配顺序回归;配合 mq-starter driver autoconfig 的 @AutoConfigureBefore 修复。同时去掉 driver 选择用例多余的 Redis 守卫。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@ import com.njcn.message.message.RecallMessage;
|
||||
import com.njcn.message.messagedto.MessageDataDTO;
|
||||
import com.njcn.middle.stream.autoconfig.RedisStreamAutoConfiguration;
|
||||
import com.njcn.mq.autoconfig.MqCoreAutoConfiguration;
|
||||
import com.njcn.mq.container.MqListenerRegistry;
|
||||
import com.njcn.mq.core.MqTemplate;
|
||||
import com.njcn.mq.driver.redis.RedisStreamMqDriver;
|
||||
import com.njcn.mq.driver.redis.RedisStreamMqDriverAutoConfiguration;
|
||||
import com.njcn.mq.driver.rocketmq.RocketMqDriver;
|
||||
@@ -15,6 +17,7 @@ import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties;
|
||||
import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.data.redis.connection.stream.MapRecord;
|
||||
@@ -38,12 +41,16 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* MQ starter 切片接入验证测试(ApplicationContextRunner,不启动全应用)。
|
||||
* 真实 Redis 要求:localhost:6379(docker redis-stream-it)。
|
||||
* 无 Redis 时,依赖 Redis 的用例通过 Assumptions 跳过。
|
||||
* 真实 Redis 要求:localhost:6379(docker redis-stream-it);仅上/下行收发用例需要,
|
||||
* 纯装配用例不需要(Lettuce 懒连接,不触发实际连接)。
|
||||
*
|
||||
* <p>注意:withConfiguration(AutoConfigurations.of(...)) 会对 autoconfig 类名做字母序排序,
|
||||
* 导致 MqCoreAutoConfiguration 在 driver autoconfig 之前处理,@ConditionalOnBean(MqDriver.class)
|
||||
* 因此失败。改用 withUserConfiguration(...) 按依赖顺序显式列出,避免排序问题。
|
||||
* <p>装配一律用 {@code withConfiguration(AutoConfigurations.of(...))},让 Spring 按
|
||||
* <b>真实 autoconfig 顺序</b>(AutoConfigurationSorter)处理,与生产环境一致。以此
|
||||
* 验证 {@link MqCoreAutoConfiguration} 的 {@code @ConditionalOnBean(MqDriver)} 能在
|
||||
* 真实顺序下拿到 driver bean —— 这依赖 driver autoconfig 上的
|
||||
* {@code @AutoConfigureBefore(MqCoreAutoConfiguration.class)}。若缺该保障,按类名字母序
|
||||
* {@code com.njcn.mq.autoconfig.*} 先于 {@code com.njcn.mq.driver.*},core 会在 driver
|
||||
* bean 注册前求值条件失败,导致 MqTemplate / MqListenerRegistry 不装配。
|
||||
*/
|
||||
public class MqSliceWiringTest {
|
||||
|
||||
@@ -58,64 +65,67 @@ public class MqSliceWiringTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* redis-stream 模式的基础 runner。
|
||||
* 按依赖顺序列出:Redis → Phase1 stream → redis driver → rocket driver(inactive) → mq core → 业务 bean
|
||||
* 这样 @ConditionalOnBean(MqDriver.class) 在 MqCoreAutoConfiguration 被解析时已能找到 MqDriver 定义。
|
||||
*/
|
||||
/** redis-stream 模式:真实 autoconfig 顺序装 5 个 autoconfig + 业务 bean(上/下行用)。 */
|
||||
private ApplicationContextRunner redisStreamRunner() {
|
||||
return new ApplicationContextRunner()
|
||||
.withUserConfiguration(
|
||||
RedisAutoConfiguration.class, // 1. StringRedisTemplate
|
||||
RedisStreamAutoConfiguration.class, // 2. StreamKeyBuilder, RedisStreamProperties
|
||||
RedisStreamMqDriverAutoConfiguration.class, // 3. MqDriver(RedisStreamMqDriver)
|
||||
RocketMqDriverAutoConfiguration.class, // 4. inactive for redis-stream
|
||||
MqCoreAutoConfiguration.class, // 5. MqTemplate, MqListenerRegistry
|
||||
FrontDataMqListener.class, // 6. 上行 listener
|
||||
RecallMqSender.class // 7. 下行 sender
|
||||
)
|
||||
.withConfiguration(AutoConfigurations.of(
|
||||
RedisAutoConfiguration.class,
|
||||
RedisStreamAutoConfiguration.class,
|
||||
RedisStreamMqDriverAutoConfiguration.class,
|
||||
RocketMqDriverAutoConfiguration.class,
|
||||
MqCoreAutoConfiguration.class))
|
||||
.withUserConfiguration(FrontDataMqListener.class, RecallMqSender.class)
|
||||
.withBean(MessAnalysisFeignClient.class,
|
||||
() -> mock(MessAnalysisFeignClient.class))
|
||||
.withPropertyValues(
|
||||
"mq.type=redis-stream",
|
||||
"spring.redis.host=localhost",
|
||||
"spring.redis.port=6379"
|
||||
);
|
||||
"spring.redis.port=6379");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 1. Driver 二选一
|
||||
// 1. 装配:mq.type 二选一 + core bean 在真实 autoconfig 顺序下装配
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
void testRedisStreamDriverSelected() {
|
||||
Assumptions.assumeTrue(redisAvailable(), "Redis not available at localhost:6379, skipping");
|
||||
redisStreamRunner().run(ctx -> {
|
||||
assertThat(ctx).hasSingleBean(MqDriver.class);
|
||||
assertThat(ctx.getBean(MqDriver.class)).isInstanceOf(RedisStreamMqDriver.class);
|
||||
});
|
||||
// 纯装配检查,不依赖真实 Redis(Lettuce 懒连接)。
|
||||
new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(
|
||||
RedisAutoConfiguration.class,
|
||||
RedisStreamAutoConfiguration.class,
|
||||
RedisStreamMqDriverAutoConfiguration.class,
|
||||
RocketMqDriverAutoConfiguration.class,
|
||||
MqCoreAutoConfiguration.class))
|
||||
.withPropertyValues(
|
||||
"mq.type=redis-stream",
|
||||
"spring.redis.host=localhost",
|
||||
"spring.redis.port=6379")
|
||||
.run(ctx -> {
|
||||
assertThat(ctx).hasSingleBean(MqDriver.class);
|
||||
assertThat(ctx.getBean(MqDriver.class)).isInstanceOf(RedisStreamMqDriver.class);
|
||||
// 真实顺序下 core 的 @ConditionalOnBean(MqDriver) 必须命中
|
||||
assertThat(ctx).hasSingleBean(MqTemplate.class);
|
||||
assertThat(ctx).hasSingleBean(MqListenerRegistry.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRocketMqDriverSelected() {
|
||||
// rocket 测试不依赖真实 Redis,仅引入 rocket 相关 + core
|
||||
// rocket 测试不依赖真实 Redis;不装 RedisAutoConfiguration,redis driver 因 mq.type 不匹配而 inactive。
|
||||
new ApplicationContextRunner()
|
||||
.withUserConfiguration(
|
||||
RocketMqDriverAutoConfiguration.class, // 1. MqDriver(RocketMqDriver)
|
||||
RedisStreamMqDriverAutoConfiguration.class, // 2. inactive for rocketmq
|
||||
MqCoreAutoConfiguration.class, // 3. MqTemplate, MqListenerRegistry
|
||||
FrontDataMqListener.class, // 4. inactive for rocketmq
|
||||
RecallMqSender.class // 5. sender
|
||||
)
|
||||
.withBean(MessAnalysisFeignClient.class,
|
||||
() -> mock(MessAnalysisFeignClient.class))
|
||||
.withBean(RocketMQTemplate.class,
|
||||
() -> mock(RocketMQTemplate.class))
|
||||
.withConfiguration(AutoConfigurations.of(
|
||||
RedisStreamMqDriverAutoConfiguration.class,
|
||||
RocketMqDriverAutoConfiguration.class,
|
||||
MqCoreAutoConfiguration.class))
|
||||
.withBean(RocketMQTemplate.class, () -> mock(RocketMQTemplate.class))
|
||||
.withBean(RocketMQProperties.class, RocketMQProperties::new)
|
||||
.withPropertyValues("mq.type=rocketmq")
|
||||
.run(ctx -> {
|
||||
assertThat(ctx).hasSingleBean(MqDriver.class);
|
||||
assertThat(ctx.getBean(MqDriver.class)).isInstanceOf(RocketMqDriver.class);
|
||||
assertThat(ctx).hasSingleBean(MqTemplate.class);
|
||||
assertThat(ctx).hasSingleBean(MqListenerRegistry.class);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user