From cdcecd0d4a2d782cd7043cd34510e6004a837fea Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 16 Mar 2022 20:47:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=20FileConfig=20=E7=9A=84?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/file/FileConfigServiceImpl.java | 3 + .../file/FileConfigServiceImplTest.java | 162 ++++++++++++++++-- .../test/resources/application-unit-test.yaml | 5 +- .../src/test/resources/sql/clean.sql | 1 + .../src/test/resources/sql/create_tables.sql | 15 ++ 5 files changed, 165 insertions(+), 21 deletions(-) diff --git a/yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java b/yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java index d74f1db0a..51cc21bbc 100755 --- a/yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-impl/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java @@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.infra.convert.file.FileConfigConvert; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper; import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.Scheduled; @@ -58,6 +59,7 @@ public class FileConfigServiceImpl implements FileConfigService { /** * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新 */ + @Getter private volatile Date maxUpdateTime; @Resource @@ -65,6 +67,7 @@ public class FileConfigServiceImpl implements FileConfigService { /** * Master FileClient 对象,有且仅有一个,即 {@link FileConfigDO#getMaster()} 对应的 */ + @Getter private FileClient masterFileClient; @Resource diff --git a/yudao-module-infra/yudao-module-infra-impl/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java b/yudao-module-infra/yudao-module-infra-impl/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java index e97df1ac6..457c0682a 100755 --- a/yudao-module-infra/yudao-module-infra-impl/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java +++ b/yudao-module-infra/yudao-module-infra-impl/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java @@ -1,25 +1,42 @@ package cn.iocoder.yudao.module.infra.service.file; +import cn.hutool.core.map.MapUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.file.core.client.FileClient; +import cn.iocoder.yudao.framework.file.core.client.FileClientConfig; +import cn.iocoder.yudao.framework.file.core.client.FileClientFactory; +import cn.iocoder.yudao.framework.file.core.client.local.LocalFileClientConfig; +import cn.iocoder.yudao.framework.file.core.enums.FileStorageEnum; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigCreateReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigUpdateReqVO; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper; +import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer; import cn.iocoder.yudao.module.infra.test.BaseDbUnitTest; -import org.junit.jupiter.api.Disabled; +import lombok.Data; import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; +import javax.validation.Validator; +import java.io.Serializable; +import java.util.Map; +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_DELETE_FAIL_MASTER; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG_NOT_EXISTS; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; /** * {@link FileConfigServiceImpl} 的单元测试类 @@ -35,10 +52,44 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { @Resource private FileConfigMapper fileConfigMapper; + @MockBean + private FileConfigProducer fileConfigProducer; + @MockBean + private Validator validator; + @MockBean + private FileClientFactory fileClientFactory; + + @Test + public void testInitLocalCache() { + // mock 数据 + FileConfigDO configDO1 = randomFileConfigDO().setId(1L).setMaster(true); + fileConfigMapper.insert(configDO1); + FileConfigDO configDO2 = randomFileConfigDO().setId(2L).setMaster(false); + fileConfigMapper.insert(configDO2); + // mock fileClientFactory 获得 master + FileClient masterFileClient = mock(FileClient.class); + when(fileClientFactory.getFileClient(eq(1L))).thenReturn(masterFileClient); + + // 调用 + fileConfigService.initFileClients(); + // 断言 fileClientFactory 调用 + verify(fileClientFactory).createOrUpdateFileClient(eq(1L), + eq(configDO1.getStorage()), eq(configDO1.getConfig())); + verify(fileClientFactory).createOrUpdateFileClient(eq(2L), + eq(configDO2.getStorage()), eq(configDO2.getConfig())); + assertSame(masterFileClient, fileConfigService.getMasterFileClient()); + // 断言 maxUpdateTime 缓存 + assertEquals(max(configDO1.getUpdateTime(), configDO2.getUpdateTime()), + fileConfigService.getMaxUpdateTime()); + } + @Test public void testCreateFileConfig_success() { // 准备参数 - FileConfigCreateReqVO reqVO = randomPojo(FileConfigCreateReqVO.class); + Map config = MapUtil.builder().put("basePath", "/yunai") + .put("domain", "https://www.iocoder.cn").build(); + FileConfigCreateReqVO reqVO = randomPojo(FileConfigCreateReqVO.class, + o -> o.setStorage(FileStorageEnum.LOCAL.getStorage()).setConfig(config)); // 调用 Long fileConfigId = fileConfigService.createFileConfig(reqVO); @@ -46,24 +97,37 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { assertNotNull(fileConfigId); // 校验记录的属性是否正确 FileConfigDO fileConfig = fileConfigMapper.selectById(fileConfigId); - assertPojoEquals(reqVO, fileConfig); + assertPojoEquals(reqVO, fileConfig, "config"); + assertFalse(fileConfig.getMaster()); + assertEquals("/yunai", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath()); + assertEquals("https://www.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain()); + // verify 调用 + verify(fileConfigProducer).sendFileConfigRefreshMessage(); } @Test public void testUpdateFileConfig_success() { // mock 数据 - FileConfigDO dbFileConfig = randomPojo(FileConfigDO.class); + FileConfigDO dbFileConfig = randomPojo(FileConfigDO.class, o -> o.setStorage(FileStorageEnum.LOCAL.getStorage()) + .setConfig(new LocalFileClientConfig().setBasePath("/yunai").setDomain("https://www.iocoder.cn"))); fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据 // 准备参数 FileConfigUpdateReqVO reqVO = randomPojo(FileConfigUpdateReqVO.class, o -> { o.setId(dbFileConfig.getId()); // 设置更新的 ID + Map config = MapUtil.builder().put("basePath", "/yunai2") + .put("domain", "https://doc.iocoder.cn").build(); + o.setConfig(config); }); // 调用 fileConfigService.updateFileConfig(reqVO); // 校验是否更新正确 FileConfigDO fileConfig = fileConfigMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, fileConfig); + assertPojoEquals(reqVO, fileConfig, "config"); + assertEquals("/yunai2", ((LocalFileClientConfig) fileConfig.getConfig()).getBasePath()); + assertEquals("https://doc.iocoder.cn", ((LocalFileClientConfig) fileConfig.getConfig()).getDomain()); + // verify 调用 + verify(fileConfigProducer).sendFileConfigRefreshMessage(); } @Test @@ -75,10 +139,33 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { assertServiceException(() -> fileConfigService.updateFileConfig(reqVO), FILE_CONFIG_NOT_EXISTS); } + @Test + public void testUpdateFileConfigMaster_success() { + // mock 数据 + FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false); + fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据 + FileConfigDO masterFileConfig = randomFileConfigDO().setMaster(true); + fileConfigMapper.insert(masterFileConfig);// @Sql: 先插入出一条存在的数据 + + // 调用 + fileConfigService.updateFileConfigMaster(dbFileConfig.getId()); + // 断言数据 + assertTrue(fileConfigMapper.selectById(dbFileConfig.getId()).getMaster()); + assertFalse(fileConfigMapper.selectById(masterFileConfig.getId()).getMaster()); + // verify 调用 + verify(fileConfigProducer).sendFileConfigRefreshMessage(); + } + + @Test + public void testUpdateFileConfigMaster_notExists() { + // 调用, 并断言异常 + assertServiceException(() -> fileConfigService.updateFileConfigMaster(randomLongId()), FILE_CONFIG_NOT_EXISTS); + } + @Test public void testDeleteFileConfig_success() { // mock 数据 - FileConfigDO dbFileConfig = randomPojo(FileConfigDO.class); + FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false); fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据 // 准备参数 Long id = dbFileConfig.getId(); @@ -87,6 +174,8 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { fileConfigService.deleteFileConfig(id); // 校验数据不存在了 assertNull(fileConfigMapper.selectById(id)); + // verify 调用 + verify(fileConfigProducer).sendFileConfigRefreshMessage(); } @Test @@ -99,27 +188,36 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { } @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testDeleteFileConfig_master() { + // mock 数据 + FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(true); + fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbFileConfig.getId(); + + // 调用, 并断言异常 + assertServiceException(() -> fileConfigService.deleteFileConfig(id), FILE_CONFIG_DELETE_FAIL_MASTER); + } + + @Test public void testGetFileConfigPage() { // mock 数据 - FileConfigDO dbFileConfig = randomPojo(FileConfigDO.class, o -> { // 等会查询到 - o.setName(null); - o.setStorage(null); - o.setCreateTime(null); - }); + FileConfigDO dbFileConfig = randomFileConfigDO().setName("芋道源码") + .setStorage(FileStorageEnum.LOCAL.getStorage()); + dbFileConfig.setCreateTime(buildTime(2022, 11, 11));// 等会查询到 fileConfigMapper.insert(dbFileConfig); // 测试 name 不匹配 - fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setName(null))); + fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setName("源码"))); // 测试 storage 不匹配 - fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setStorage(null))); + fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setStorage(FileStorageEnum.DB.getStorage()))); // 测试 createTime 不匹配 - fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setCreateTime(null))); + fileConfigMapper.insert(cloneIgnoreId(dbFileConfig, o -> o.setCreateTime(buildTime(2022, 12, 12)))); // 准备参数 FileConfigPageReqVO reqVO = new FileConfigPageReqVO(); - reqVO.setName(null); - reqVO.setStorage(null); - reqVO.setBeginCreateTime(null); - reqVO.setEndCreateTime(null); + reqVO.setName("芋道"); + reqVO.setStorage(FileStorageEnum.LOCAL.getStorage()); + reqVO.setBeginCreateTime(buildTime(2022, 11, 10)); + reqVO.setEndCreateTime(buildTime(2022, 11, 12)); // 调用 PageResult pageResult = fileConfigService.getFileConfigPage(reqVO); @@ -129,4 +227,30 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest { assertPojoEquals(dbFileConfig, pageResult.getList().get(0)); } + @Test + public void testFileConfig() { + // mock 数据 + FileConfigDO dbFileConfig = randomFileConfigDO().setMaster(false); + fileConfigMapper.insert(dbFileConfig);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbFileConfig.getId(); + // mock 获得 Client + FileClient fileClient = mock(FileClient.class); + when(fileClientFactory.getFileClient(eq(id))).thenReturn(fileClient); + when(fileClient.upload(any(), any())).thenReturn("https://www.iocoder.cn"); + + // 调用,并断言 + assertEquals("https://www.iocoder.cn", fileConfigService.testFileConfig(id)); + } + + private FileConfigDO randomFileConfigDO() { + return randomPojo(FileConfigDO.class).setStorage(randomEle(FileStorageEnum.values()).getStorage()) + .setConfig(new EmptyFileClientConfig()); + } + + @Data + public static class EmptyFileClientConfig implements FileClientConfig, Serializable { + + } + } diff --git a/yudao-module-infra/yudao-module-infra-impl/src/test/resources/application-unit-test.yaml b/yudao-module-infra/yudao-module-infra-impl/src/test/resources/application-unit-test.yaml index 60914d97f..3a2079cdc 100644 --- a/yudao-module-infra/yudao-module-infra-impl/src/test/resources/application-unit-test.yaml +++ b/yudao-module-infra/yudao-module-infra-impl/src/test/resources/application-unit-test.yaml @@ -26,8 +26,9 @@ spring: port: 16379 # 端口(单元测试,使用 16379 端口) database: 0 # 数据库索引 -mybatis: +mybatis-plus: lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject --- #################### 定时任务相关配置 #################### @@ -46,4 +47,4 @@ mybatis: # 芋道配置项,设置当前项目所有自定义的配置 yudao: info: - base-package: cn.iocoder.yudao.module + base-package: cn.iocoder.yudao diff --git a/yudao-module-infra/yudao-module-infra-impl/src/test/resources/sql/clean.sql b/yudao-module-infra/yudao-module-infra-impl/src/test/resources/sql/clean.sql index 071c77bc7..cc8316837 100644 --- a/yudao-module-infra/yudao-module-infra-impl/src/test/resources/sql/clean.sql +++ b/yudao-module-infra/yudao-module-infra-impl/src/test/resources/sql/clean.sql @@ -8,3 +8,4 @@ DELETE FROM "infra_api_access_log"; DELETE FROM "infra_file"; DELETE FROM "infra_api_error_log"; DELETE FROM "infra_test_demo"; +DELETE FROM "infra_file_config"; diff --git a/yudao-module-infra/yudao-module-infra-impl/src/test/resources/sql/create_tables.sql b/yudao-module-infra/yudao-module-infra-impl/src/test/resources/sql/create_tables.sql index 80213b4a7..0265690d0 100644 --- a/yudao-module-infra/yudao-module-infra-impl/src/test/resources/sql/create_tables.sql +++ b/yudao-module-infra/yudao-module-infra-impl/src/test/resources/sql/create_tables.sql @@ -16,6 +16,21 @@ CREATE TABLE IF NOT EXISTS "infra_config" ( PRIMARY KEY ("id") ) COMMENT '参数配置表'; +CREATE TABLE IF NOT EXISTS "infra_file_config" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(63) NOT NULL, + "storage" tinyint NOT NULL, + "remark" varchar(255), + "master" bit(1) NOT NULL, + "config" varchar(4096) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '文件配置表'; + CREATE TABLE IF NOT EXISTS "infra_file" ( "id" varchar(188) NOT NULL, "type" varchar(63) DEFAULT NULL,