chore: 修改生成ID的测试类

This commit is contained in:
CaptainB 2023-10-27 13:36:21 +08:00 committed by 刘瑞斌
parent d17f729419
commit 4894976eee
8 changed files with 190 additions and 53 deletions

View File

@ -0,0 +1,19 @@
package io.metersphere.sdk.constants;
public enum ApplicationNumScope {
API_DEFINITION,
API_TEST_CASE,
API_SCENARIO,
UI_SCENARIO,
UI_ELEMENT,
UI_CUSTOM_COMMAND,
LOAD_TEST,
TEST_PLAN,
BUG_MANAGEMENT,
CASE_MANAGEMENT
}

View File

@ -20,27 +20,25 @@ public class ProjectApplicationType {
} }
//UI测试 //UI测试
public enum UI{ public enum UI {
UI_CLEAN_REPORT, UI_CLEAN_REPORT,
UI_SHARE_REPORT, UI_SHARE_REPORT,
UI_RESOURCE_POOL_ID, UI_RESOURCE_POOL_ID,
} }
//性能测试 //性能测试
public enum PERFORMANCE_TEST{ public enum LOAD_TEST {
PERFORMANCE_TEST_CLEAN_REPORT, LOAD_TEST_CLEAN_REPORT,
PERFORMANCE_TEST_SHARE_REPORT, LOAD_TEST_SHARE_REPORT,
PERFORMANCE_TEST_SCRIPT_REVIEWER_ENABLE, LOAD_TEST_SCRIPT_REVIEWER_ENABLE,
PERFORMANCE_TEST_SCRIPT_REVIEWER_ID, LOAD_TEST_SCRIPT_REVIEWER_ID,
} }
//接口测试 //接口测试
public enum API{ public enum API {
API_URL_REPEATABLE, API_URL_REPEATABLE,
API_CLEAN_REPORT, API_CLEAN_REPORT,
API_SHARE_REPORT, API_SHARE_REPORT,
@ -53,40 +51,40 @@ public class ProjectApplicationType {
//用例管理 //用例管理
public enum CASE{ public enum CASE {
CASE_PUBLIC, CASE_PUBLIC,
CASE_RE_REVIEW, CASE_RE_REVIEW,
} }
//用例管理-关联需求 //用例管理-关联需求
public enum CASE_RELATED_CONFIG{ public enum CASE_RELATED_CONFIG {
CASE_RELATED, CASE_RELATED,
CASE_ENABLE, CASE_ENABLE,
} }
//缺陷管理 //缺陷管理
public enum BUG{ public enum BUG {
BUG_SYNC BUG_SYNC
} }
//缺陷管理-同步配置项 //缺陷管理-同步配置项
public enum BUG_SYNC_CONFIG{ public enum BUG_SYNC_CONFIG {
CRON_EXPRESSION, CRON_EXPRESSION,
SYNC_ENABLE, SYNC_ENABLE,
MECHANISM, MECHANISM,
} }
// 版本管理-配置项 // 版本管理-配置项
public enum VERSION{ public enum VERSION {
VERSION_ENABLE VERSION_ENABLE
} }
/** /**
* 记录项目中配置的默认模板 * 记录项目中配置的默认模板
*/ */
public enum DEFAULT_TEMPLATE{ public enum DEFAULT_TEMPLATE {
FUNCTIONAL_DEFAULT_TEMPLATE, FUNCTIONAL_DEFAULT_TEMPLATE,
BUG_DEFAULT_TEMPLATE, BUG_DEFAULT_TEMPLATE,
API_DEFAULT_TEMPLATE, API_DEFAULT_TEMPLATE,

View File

@ -105,7 +105,7 @@ public class ProjectApplicationController {
@Operation(summary = "性能测试-获取配置") @Operation(summary = "性能测试-获取配置")
@RequiresPermissions(PermissionConstants.PROJECT_APPLICATION_PERFORMANCE_TEST_READ) @RequiresPermissions(PermissionConstants.PROJECT_APPLICATION_PERFORMANCE_TEST_READ)
public Map<String, Object> getPerformanceTest(@Validated @RequestBody ProjectApplicationRequest request) { public Map<String, Object> getPerformanceTest(@Validated @RequestBody ProjectApplicationRequest request) {
List<String> types = Arrays.asList(ProjectApplicationType.PERFORMANCE_TEST.values()).stream().map(ProjectApplicationType.PERFORMANCE_TEST::name).collect(Collectors.toList()); List<String> types = Arrays.asList(ProjectApplicationType.LOAD_TEST.values()).stream().map(ProjectApplicationType.LOAD_TEST::name).collect(Collectors.toList());
return projectApplicationService.get(request, types); return projectApplicationService.get(request, types);
} }

View File

@ -108,7 +108,7 @@ public class ProjectApplicationService {
String type = application.getType(); String type = application.getType();
if (StringUtils.equals(type, ProjectApplicationType.TEST_PLAN.TEST_PLAN_CLEAN_REPORT.name()) if (StringUtils.equals(type, ProjectApplicationType.TEST_PLAN.TEST_PLAN_CLEAN_REPORT.name())
|| StringUtils.equals(type, ProjectApplicationType.UI.UI_CLEAN_REPORT.name()) || StringUtils.equals(type, ProjectApplicationType.UI.UI_CLEAN_REPORT.name())
|| StringUtils.equals(type, ProjectApplicationType.PERFORMANCE_TEST.PERFORMANCE_TEST_CLEAN_REPORT.name()) || StringUtils.equals(type, ProjectApplicationType.LOAD_TEST.LOAD_TEST_CLEAN_REPORT.name())
|| StringUtils.equals(type, ProjectApplicationType.API.API_CLEAN_REPORT.name())) { || StringUtils.equals(type, ProjectApplicationType.API.API_CLEAN_REPORT.name())) {
//清除 测试计划/UI测试/性能测试/接口测试 报告 定时任务 //清除 测试计划/UI测试/性能测试/接口测试 报告 定时任务
this.doHandleSchedule(application, currentUser); this.doHandleSchedule(application, currentUser);

View File

@ -177,7 +177,7 @@ public class ProjectApplicationControllerTests extends BaseTest {
@Order(7) @Order(7)
public void testPerformanceClean() throws Exception { public void testPerformanceClean() throws Exception {
//新增 //新增
ProjectApplication request = creatRequest(ProjectApplicationType.PERFORMANCE_TEST.PERFORMANCE_TEST_CLEAN_REPORT.name(), TIME_TYPE_VALUE); ProjectApplication request = creatRequest(ProjectApplicationType.LOAD_TEST.LOAD_TEST_CLEAN_REPORT.name(), TIME_TYPE_VALUE);
this.requestPost(PERFORMANCE_UPDATE_URL, request); this.requestPost(PERFORMANCE_UPDATE_URL, request);
//更新 //更新
request.setTypeValue("4M"); request.setTypeValue("4M");
@ -192,7 +192,7 @@ public class ProjectApplicationControllerTests extends BaseTest {
@Order(8) @Order(8)
public void testPerformanceShare() throws Exception { public void testPerformanceShare() throws Exception {
//新增 //新增
ProjectApplication request = creatRequest(ProjectApplicationType.PERFORMANCE_TEST.PERFORMANCE_TEST_SHARE_REPORT.name(), TIME_TYPE_VALUE); ProjectApplication request = creatRequest(ProjectApplicationType.LOAD_TEST.LOAD_TEST_SHARE_REPORT.name(), TIME_TYPE_VALUE);
this.requestPost(PERFORMANCE_UPDATE_URL, request); this.requestPost(PERFORMANCE_UPDATE_URL, request);
//更新 //更新
request.setTypeValue("5M"); request.setTypeValue("5M");
@ -204,7 +204,7 @@ public class ProjectApplicationControllerTests extends BaseTest {
@Order(9) @Order(9)
public void testPerformanceReviewer() throws Exception { public void testPerformanceReviewer() throws Exception {
//新增 //新增
ProjectApplication request = creatRequest(ProjectApplicationType.PERFORMANCE_TEST.PERFORMANCE_TEST_SCRIPT_REVIEWER_ENABLE.name(), "admin"); ProjectApplication request = creatRequest(ProjectApplicationType.LOAD_TEST.LOAD_TEST_SCRIPT_REVIEWER_ENABLE.name(), "admin");
this.requestPost(PERFORMANCE_UPDATE_URL, request); this.requestPost(PERFORMANCE_UPDATE_URL, request);
} }

View File

@ -0,0 +1,76 @@
package io.metersphere.system.uid;
import com.fit2cloud.quartz.anno.QuartzScheduled;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.util.LogUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RIdGenerator;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
@Component
public class NumGenerator {
private static final long INIT = 100001L; // 代表从100001开始各种domain的 num
private static final long LIMIT = 1;
private static Redisson redisson;
private static StringRedisTemplate stringRedisTemplate;
private static ProjectMapper projectMapper;
public NumGenerator(Redisson redisson, StringRedisTemplate stringRedisTemplate, ProjectMapper projectMapper) {
NumGenerator.redisson = redisson;
NumGenerator.stringRedisTemplate = stringRedisTemplate;
NumGenerator.projectMapper = projectMapper;
}
/**
* @param prefix 前缀: PROJECT_ID, 或者 PROJECT_ID + "_" + DOMAIN 例如接口用例的前缀为: 100001_12345
* @param scope 用例类型
*/
public static long nextNum(String prefix, ApplicationNumScope scope) {
RIdGenerator idGenerator = redisson.getIdGenerator(prefix + "_" + scope.name());
// 每次都尝试初始化容量为1只有一个线程可以初始化成功
if (scope.equals(ApplicationNumScope.API_TEST_CASE)) {
// 二级的用例
idGenerator.tryInit(Long.parseLong(prefix.split("_")[1] + INIT), LIMIT);
} else {
idGenerator.tryInit(INIT, LIMIT);
}
return idGenerator.nextId();
}
@QuartzScheduled(cron = "0 1 0 * * ?")
public void cleanDeletedProjectResource() {
for (ApplicationNumScope value : ApplicationNumScope.values()) {
cleanDeletedProjectResource(value);
}
}
private void cleanDeletedProjectResource(ApplicationNumScope value) {
String suffix = "}:allocation";
ScanOptions options = ScanOptions.scanOptions().match("*_" + value.name()).count(1000).build();
try (
Cursor<String> scan = stringRedisTemplate.scan(options)
) {
while (scan.hasNext()) {
String key = scan.next();
if (StringUtils.contains(key, suffix)) {
continue;
}
Project project = projectMapper.selectByPrimaryKey(key.split("_")[0]);
if (project == null) {
LogUtils.info("清理已经删除项目的num数据: " + key);
stringRedisTemplate.delete(key);
stringRedisTemplate.delete("{" + key + suffix);
}
}
}
}
}

View File

@ -1,33 +0,0 @@
package io.metersphere.system.controller;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.redisson.Redisson;
import org.redisson.api.RIdGenerator;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TestRIdGenerator {
@Resource
private Redisson redisson;
@Test
public void testId1() throws Exception {
String projectId = "projectId";
RIdGenerator idGenerator = redisson.getIdGenerator(projectId);
long capacity = 1000000; // 容量代表每个项目最多可以生成多少个id
long init = 1000000_1000001L; // 代表从100_0000_100_0001开始项目的num
idGenerator.tryInit(init, capacity);
long nextId = idGenerator.nextId();
Assertions.assertEquals(nextId, init);
// for (int i = 0; i < capacity; i++) {
// long nextId = idGenerator.nextId();
// }
}
}

View File

@ -0,0 +1,77 @@
package io.metersphere.system.rid;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.util.CommonBeanFactory;
import io.metersphere.system.uid.NumGenerator;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class RIdGeneratorTests {
@Test
@Order(1)
public void testId1() throws Exception {
String projectId = "100001";
long capacity = 10; // 容量代表每个项目最多可以生成多少个id
long init = 100001L; // 代表从1000001开始项目的 num
long start = System.currentTimeMillis();
AtomicLong atomicLong = new AtomicLong(init);
// 使用多线程执行
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < capacity; i++) {
executorService.submit(() -> {
long nextId = NumGenerator.nextNum(projectId, ApplicationNumScope.API_DEFINITION);
System.out.println(nextId);
if (atomicLong.get() < nextId) {
atomicLong.set(nextId);
}
});
}
executorService.close();
System.out.println("耗时: " + (System.currentTimeMillis() - start) + "ms");
Assertions.assertEquals(capacity + init, atomicLong.get() + 1);
}
@Test
@Order(2)
public void testId2() throws Exception {
String projectId = "100001";
long capacity = 10; // 容量代表每个项目最多可以生成多少个id
long init = 100001L; // 代表从1000001开始项目的 num
long apiNum = 100005;
long start = System.currentTimeMillis();
AtomicLong atomicLong = new AtomicLong(init);
// 使用多线程执行
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < capacity; i++) {
executorService.submit(() -> {
// 接口用例的前缀为: PROJECT_ID_API_DEFINITION 比较特殊
long nextId = NumGenerator.nextNum(projectId + "_" + apiNum, ApplicationNumScope.API_TEST_CASE);
System.out.println(nextId);
if (atomicLong.get() < nextId) {
atomicLong.set(nextId);
}
});
}
executorService.close();
System.out.println("耗时: " + (System.currentTimeMillis() - start) + "ms");
Assertions.assertEquals(capacity + Long.parseLong(apiNum + "" + init), atomicLong.get() + 1);
}
@Test
@Order(3)
public void testClean() {
NumGenerator numGenerator = CommonBeanFactory.getBean(NumGenerator.class);
numGenerator.cleanDeletedProjectResource();
}
}