Prechádzať zdrojové kódy

完善 FileConfig 的单元测试

YunaiV 3 rokov pred
rodič
commit
cdcecd0d4a

+ 3 - 0
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

+ 143 - 19
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<String, Object> config = MapUtil.<String, Object>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<String, Object> config = MapUtil.<String, Object>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<FileConfigDO> 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 {
+
+    }
+
 }

+ 3 - 2
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

+ 1 - 0
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";

+ 15 - 0
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,