package com.tcm.training.admin.bootstrap; import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; import org.junit.jupiter.api.Test; import org.springframework.boot.DefaultApplicationArguments; import org.springframework.beans.factory.ObjectProvider; import java.util.LinkedHashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * 海诊通动态数据源启动校验测试。 */ class HaizhentongDataSourceStartupValidatorTest { /** * 缺少海诊通数据源配置时应在启动阶段直接失败。 */ @Test void runShouldFailWhenHaizhentongConfigMissing() { DynamicDataSourceProperties properties = buildProperties(false); HaizhentongDataSourceStartupValidator validator = new HaizhentongDataSourceStartupValidator( properties, emptyProvider() ); IllegalStateException exception = assertThrows(IllegalStateException.class, () -> validator.run(new DefaultApplicationArguments(new String[0]))); assertEquals("海诊通数据源 haizhentong 未配置,请检查当前激活环境的配置文件", exception.getMessage()); } /** * 配置存在但运行期未注册海诊通数据源时也应直接失败。 */ @Test void runShouldFailWhenHaizhentongDatasourceNotRegistered() { DynamicDataSourceProperties properties = buildProperties(true); DynamicRoutingDataSource routingDataSource = mock(DynamicRoutingDataSource.class); when(routingDataSource.getDataSources()).thenReturn(new LinkedHashMap()); HaizhentongDataSourceStartupValidator validator = new HaizhentongDataSourceStartupValidator( properties, fixedProvider(routingDataSource) ); IllegalStateException exception = assertThrows(IllegalStateException.class, () -> validator.run(new DefaultApplicationArguments(new String[0]))); assertEquals("海诊通数据源 haizhentong 未注册成功,请检查多数据源配置", exception.getMessage()); } /** * 配置与运行期注册都完整时应通过启动校验。 */ @Test void runShouldPassWhenHaizhentongDatasourceConfiguredAndRegistered() { DynamicDataSourceProperties properties = buildProperties(true); DynamicRoutingDataSource routingDataSource = mock(DynamicRoutingDataSource.class); Map dataSourceMap = new LinkedHashMap(); dataSourceMap.put("master", mock(javax.sql.DataSource.class)); dataSourceMap.put("haizhentong", mock(javax.sql.DataSource.class)); when(routingDataSource.getDataSources()).thenReturn(dataSourceMap); HaizhentongDataSourceStartupValidator validator = new HaizhentongDataSourceStartupValidator( properties, fixedProvider(routingDataSource) ); assertDoesNotThrow(() -> validator.run(new DefaultApplicationArguments(new String[0]))); } /** * 存在海诊通配置但当前上下文拿不到动态路由 Bean 时,应跳过运行期校验而不是阻断启动。 */ @Test void runShouldPassWhenRoutingDatasourceBeanUnavailable() { DynamicDataSourceProperties properties = buildProperties(true); HaizhentongDataSourceStartupValidator validator = new HaizhentongDataSourceStartupValidator( properties, emptyProvider() ); assertDoesNotThrow(() -> validator.run(new DefaultApplicationArguments(new String[0]))); } /** * 构造动态数据源配置对象,模拟 application.yml 中的多数据源声明。 */ private DynamicDataSourceProperties buildProperties(boolean includeHaizhentong) { DynamicDataSourceProperties properties = new DynamicDataSourceProperties(); Map datasource = new LinkedHashMap(); datasource.put("master", new DataSourceProperty()); if (includeHaizhentong) { datasource.put("haizhentong", new DataSourceProperty()); } properties.setDatasource(datasource); return properties; } /** * 构造返回固定 Bean 的 ObjectProvider 测试桩。 */ private ObjectProvider fixedProvider(DynamicRoutingDataSource routingDataSource) { return new SimpleObjectProvider(routingDataSource); } /** * 构造返回空值的 ObjectProvider 测试桩。 */ private ObjectProvider emptyProvider() { return new SimpleObjectProvider(null); } /** * 以最小实现满足测试对 ObjectProvider 的使用。 */ private static class SimpleObjectProvider implements ObjectProvider { private final DynamicRoutingDataSource value; /** * 注入当前测试需要暴露的动态路由 Bean。 */ private SimpleObjectProvider(DynamicRoutingDataSource value) { this.value = value; } /** * 按固定值返回 Bean,模拟 Spring 容器中的直接获取。 */ @Override public DynamicRoutingDataSource getObject() { return value; } /** * 按固定值返回 Bean,模拟 Spring 容器中的可选注入。 */ @Override public DynamicRoutingDataSource getObject(Object... args) { return value; } /** * 按固定值返回 Bean,模拟 Spring 容器中的可选注入。 */ @Override public DynamicRoutingDataSource getIfAvailable() { return value; } /** * 按固定值返回 Bean,模拟 Spring 容器中的唯一候选。 */ @Override public DynamicRoutingDataSource getIfUnique() { return value; } /** * 返回简单迭代结果,满足 ObjectProvider 的接口约束。 */ @Override public java.util.Iterator iterator() { return value == null ? java.util.Collections.emptyList().iterator() : java.util.Collections.singletonList(value).iterator(); } } }