diff --git a/orion-ops-dependencies/pom.xml b/orion-ops-dependencies/pom.xml
index a0357a51..84c77fc9 100644
--- a/orion-ops-dependencies/pom.xml
+++ b/orion-ops-dependencies/pom.xml
@@ -29,6 +29,9 @@
3.18.0
2.7.10
2.14.2
+ 4.11.0
+ 1.0.7
+ 7.2.11.RELEASE
@@ -125,6 +128,11 @@
orion-ops-spring-boot-starter-monitor
${revision}
+
+ com.orion.ops
+ orion-ops-spring-boot-starter-test
+ ${revision}
+
@@ -241,6 +249,47 @@
${transmittable-thread-local.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring.boot.version}
+
+
+ asm
+ org.ow2.asm
+
+
+ org.mockito
+ mockito-core
+
+
+ com.vaadin.external.google
+ android-json
+
+
+
+
+
+
+ org.mockito
+ mockito-inline
+ ${mockito-inline.version}
+
+
+
+
+ com.github.fppt
+ jedis-mock
+ ${jedis-mock.version}
+
+
+
+
+ uk.co.jemos.podam
+ podam
+ ${podam.version}
+
diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-datasource/src/main/java/com/orion/ops/framework/datasource/config/OrionDatasourceAutoConfiguration.java b/orion-ops-framework/orion-ops-spring-boot-starter-datasource/src/main/java/com/orion/ops/framework/datasource/config/OrionDataSourceAutoConfiguration.java
similarity index 97%
rename from orion-ops-framework/orion-ops-spring-boot-starter-datasource/src/main/java/com/orion/ops/framework/datasource/config/OrionDatasourceAutoConfiguration.java
rename to orion-ops-framework/orion-ops-spring-boot-starter-datasource/src/main/java/com/orion/ops/framework/datasource/config/OrionDataSourceAutoConfiguration.java
index a96a3b6f..d0468a72 100644
--- a/orion-ops-framework/orion-ops-spring-boot-starter-datasource/src/main/java/com/orion/ops/framework/datasource/config/OrionDatasourceAutoConfiguration.java
+++ b/orion-ops-framework/orion-ops-spring-boot-starter-datasource/src/main/java/com/orion/ops/framework/datasource/config/OrionDataSourceAutoConfiguration.java
@@ -22,7 +22,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@AutoConfigureOrder(AutoConfigureOrderConst.FRAMEWORK_DATASOURCE)
@EnableTransactionManagement(proxyTargetClass = true)
@EnableConfigurationProperties(DruidStatProperties.class)
-public class OrionDatasourceAutoConfiguration {
+public class OrionDataSourceAutoConfiguration {
/**
* @param properties 配置
diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-datasource/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/orion-ops-framework/orion-ops-spring-boot-starter-datasource/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 7611e1ca..85d18ef4 100644
--- a/orion-ops-framework/orion-ops-spring-boot-starter-datasource/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/orion-ops-framework/orion-ops-spring-boot-starter-datasource/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1 @@
-com.orion.ops.framework.datasource.config.OrionDatasourceAutoConfiguration
\ No newline at end of file
+com.orion.ops.framework.datasource.config.OrionDataSourceAutoConfiguration
\ No newline at end of file
diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/config/OrionMybatisAutoConfiguration.java b/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/config/OrionMybatisAutoConfiguration.java
index 5a939b32..b18b44ea 100644
--- a/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/config/OrionMybatisAutoConfiguration.java
+++ b/orion-ops-framework/orion-ops-spring-boot-starter-mybatis/src/main/java/com/orion/ops/framework/mybatis/config/OrionMybatisAutoConfiguration.java
@@ -12,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
@@ -31,6 +32,7 @@ public class OrionMybatisAutoConfiguration {
* @return 字段填充元数据处理器
*/
@Bean
+ @ConditionalOnBean(SecurityHolder.class)
public MetaObjectHandler defaultMetaObjectHandler(SecurityHolder securityHolder) {
// 设置填充工具参数
DomainFillUtils.setSecurityHolder(securityHolder);
diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-test/pom.xml b/orion-ops-framework/orion-ops-spring-boot-starter-test/pom.xml
new file mode 100644
index 00000000..4a1d1179
--- /dev/null
+++ b/orion-ops-framework/orion-ops-spring-boot-starter-test/pom.xml
@@ -0,0 +1,66 @@
+
+
+
+ com.orion.ops
+ orion-ops-framework
+ ${revision}
+
+
+ 4.0.0
+ orion-ops-spring-boot-starter-test
+ ${project.artifactId}
+ jar
+
+ 项目单元测试包
+ https://github.com/lijiahangmax/orion-ops-pro
+
+
+
+ com.orion.ops
+ orion-ops-common
+
+
+
+ com.orion.ops
+ orion-ops-spring-boot-starter-mybatis
+
+
+
+ com.orion.ops
+ orion-ops-spring-boot-starter-datasource
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+
+
+ org.mockito
+ mockito-inline
+
+
+
+
+ com.h2database
+ h2
+
+
+
+
+ com.github.fppt
+ jedis-mock
+
+
+
+
+ uk.co.jemos.podam
+ podam
+
+
+
+
\ No newline at end of file
diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/config/OrionH2SqlInitializationTestConfiguration.java b/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/config/OrionH2SqlInitializationTestConfiguration.java
new file mode 100644
index 00000000..1757b0cf
--- /dev/null
+++ b/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/config/OrionH2SqlInitializationTestConfiguration.java
@@ -0,0 +1,53 @@
+package com.orion.ops.framework.test.config;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
+import org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
+import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
+import org.springframework.boot.sql.init.DatabaseInitializationSettings;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Profile;
+
+import javax.sql.DataSource;
+
+/**
+ * 单元测试 H2 数据库 DML 初始化脚本
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023/8/23 17:17
+ */
+@Profile("unit-test")
+@Lazy(value = false)
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnMissingBean(AbstractScriptDatabaseInitializer.class)
+@ConditionalOnSingleCandidate(DataSource.class)
+@ConditionalOnClass(name = "org.springframework.jdbc.datasource.init.DatabasePopulator")
+@EnableConfigurationProperties(SqlInitializationProperties.class)
+public class OrionH2SqlInitializationTestConfiguration {
+
+ /**
+ * 数据源脚本初始化 Bean
+ */
+ @Bean
+ public DataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource,
+ SqlInitializationProperties initializationProperties) {
+ // TODO 看看正常情况下会不会有
+ // 初始化配置
+ DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
+ settings.setSchemaLocations(initializationProperties.getSchemaLocations());
+ settings.setDataLocations(initializationProperties.getDataLocations());
+ settings.setContinueOnError(initializationProperties.isContinueOnError());
+ settings.setSeparator(initializationProperties.getSeparator());
+ settings.setEncoding(initializationProperties.getEncoding());
+ settings.setMode(initializationProperties.getMode());
+ // 初始化
+ return new DataSourceScriptDatabaseInitializer(dataSource, settings);
+ }
+
+}
diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/config/OrionMockRedisTestConfiguration.java b/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/config/OrionMockRedisTestConfiguration.java
new file mode 100644
index 00000000..ed4d60f7
--- /dev/null
+++ b/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/config/OrionMockRedisTestConfiguration.java
@@ -0,0 +1,38 @@
+package com.orion.ops.framework.test.config;
+
+import com.github.fppt.jedismock.RedisServer;
+import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Profile;
+
+/**
+ * 单元测试 redis 初始化
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023/8/23 17:19
+ */
+@Lazy(false)
+@Profile("unit-test")
+@Configuration(proxyBeanMethods = false)
+@EnableConfigurationProperties(RedisProperties.class)
+public class OrionMockRedisTestConfiguration {
+
+ /**
+ * mockRedisServer
+ */
+ @Bean
+ public RedisServer redisServer(RedisProperties properties) {
+ // TODO 看看正常情况下会不会有
+ RedisServer redisServer = new RedisServer(properties.getPort());
+ try {
+ redisServer.start();
+ } catch (Exception ignore) {
+ }
+ return redisServer;
+ }
+
+}
diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/core/base/BaseUnitTest.java b/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/core/base/BaseUnitTest.java
new file mode 100644
index 00000000..fc69c90f
--- /dev/null
+++ b/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/core/base/BaseUnitTest.java
@@ -0,0 +1,46 @@
+package com.orion.ops.framework.test.core.base;
+
+import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
+import com.orion.ops.framework.datasource.config.OrionDataSourceAutoConfiguration;
+import com.orion.ops.framework.mybatis.config.OrionMybatisAutoConfiguration;
+import com.orion.ops.framework.test.config.OrionH2SqlInitializationTestConfiguration;
+import com.orion.ops.framework.test.config.OrionMockRedisTestConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.annotation.Rollback;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.jdbc.Sql;
+
+/**
+ * 单元测试父类
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023/8/23 15:12
+ */
+@Rollback
+@ActiveProfiles("unit-test")
+@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseUnitTest.Application.class)
+public class BaseUnitTest {
+
+ @Import({
+ // datasource
+ OrionDataSourceAutoConfiguration.class,
+ DataSourceAutoConfiguration.class,
+ DataSourceTransactionManagerAutoConfiguration.class,
+ OrionH2SqlInitializationTestConfiguration.class,
+ DruidDataSourceAutoConfigure.class,
+ // mybatis
+ OrionMybatisAutoConfiguration.class,
+ MybatisPlusAutoConfiguration.class,
+ // redis
+ OrionMockRedisTestConfiguration.class,
+ })
+ public static class Application {
+ }
+
+}
diff --git a/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/core/utils/EntityRandoms.java b/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/core/utils/EntityRandoms.java
new file mode 100644
index 00000000..4e03d870
--- /dev/null
+++ b/orion-ops-framework/orion-ops-spring-boot-starter-test/src/main/java/com/orion/ops/framework/test/core/utils/EntityRandoms.java
@@ -0,0 +1,86 @@
+package com.orion.ops.framework.test.core.utils;
+
+import com.orion.lang.utils.Arrays1;
+import com.orion.lang.utils.random.Randoms;
+import uk.co.jemos.podam.api.PodamFactory;
+import uk.co.jemos.podam.api.PodamFactoryImpl;
+
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 对象生成器
+ *
+ * @author Jiahang Li
+ * @version 1.0.0
+ * @since 2023/8/23 14:13
+ */
+public class EntityRandoms {
+
+ private EntityRandoms() {
+ }
+
+ private static final int RANDOM_STRING_LENGTH = 5;
+
+ private static final int RANDOM_INT_MAX = 10;
+
+ private static final int RANDOM_COLLECTION_LENGTH = 5;
+
+ private static final String DELETED = "deleted";
+
+ private static final PodamFactory FACTORY = new PodamFactoryImpl();
+
+ static {
+ // 字符串
+ FACTORY.getStrategy().addOrReplaceTypeManufacturer(String.class, (dataProviderStrategy, attributeMetadata, map) -> Randoms.randomLetter(RANDOM_STRING_LENGTH));
+ // Integer
+ FACTORY.getStrategy().addOrReplaceTypeManufacturer(Integer.class, (dataProviderStrategy, attributeMetadata, map) -> Randoms.randomInt(0, RANDOM_INT_MAX));
+ // Boolean
+ FACTORY.getStrategy().addOrReplaceTypeManufacturer(Boolean.class, (dataProviderStrategy, attributeMetadata, map) -> {
+ if (DELETED.equals(attributeMetadata.getAttributeName())) {
+ return false;
+ }
+ return Randoms.randomBoolean();
+ });
+ }
+
+ @SafeVarargs
+ public static T random(Class clazz, Consumer... consumers) {
+ T e = FACTORY.manufacturePojo(clazz);
+ if (Arrays1.isNotEmpty(consumers)) {
+ Arrays.stream(consumers).forEach(consumer -> consumer.accept(e));
+ }
+ return e;
+ }
+
+ @SafeVarargs
+ public static T random(Class clazz, Type type, Consumer... consumers) {
+ T e = FACTORY.manufacturePojo(clazz, type);
+ if (Arrays1.isNotEmpty(consumers)) {
+ Arrays.stream(consumers).forEach(consumer -> consumer.accept(e));
+ }
+ return e;
+ }
+
+ @SafeVarargs
+ public static Set randomSet(Class clazz, Consumer... consumers) {
+ return Stream.iterate(0, i -> i)
+ .limit(Randoms.randomInt(1, RANDOM_COLLECTION_LENGTH))
+ .map(o -> random(clazz, consumers))
+ .collect(Collectors.toSet());
+ }
+
+ @SafeVarargs
+ public static List randomList(Class clazz, Consumer... consumers) {
+ return Stream.iterate(0, i -> i)
+ .limit(Randoms.randomInt(1, RANDOM_COLLECTION_LENGTH))
+ .map(o -> random(clazz, consumers))
+ .collect(Collectors.toList());
+ }
+
+}
diff --git a/orion-ops-framework/pom.xml b/orion-ops-framework/pom.xml
index 25fd3ca0..ca365b7f 100644
--- a/orion-ops-framework/pom.xml
+++ b/orion-ops-framework/pom.xml
@@ -30,6 +30,7 @@
orion-ops-spring-boot-starter-storage
orion-ops-spring-boot-starter-security
orion-ops-spring-boot-starter-monitor
+ orion-ops-spring-boot-starter-test
\ No newline at end of file