mirror of
https://github.com/ccmjga/zhilu-admin
synced 2026-04-14 13:23:39 +00:00
add curl field
This commit is contained in:
@@ -6,18 +6,24 @@ import com.zl.mjga.annotation.SkipAopLog;
|
|||||||
import com.zl.mjga.repository.UserRepository;
|
import com.zl.mjga.repository.UserRepository;
|
||||||
import com.zl.mjga.service.AopLogService;
|
import com.zl.mjga.service.AopLogService;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Around;
|
||||||
import org.aspectj.lang.annotation.Aspect;
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
import org.aspectj.lang.reflect.MethodSignature;
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
import org.jooq.generated.mjga.tables.pojos.AopLog;
|
import org.jooq.generated.mjga.tables.pojos.AopLog;
|
||||||
import org.jooq.generated.mjga.tables.pojos.User;
|
import org.jooq.generated.mjga.tables.pojos.User;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@@ -28,6 +34,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
|
|||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@ConditionalOnProperty(name = "aop.logging.enabled", havingValue = "true", matchIfMissing = true)
|
||||||
public class LoggingAspect {
|
public class LoggingAspect {
|
||||||
|
|
||||||
private final AopLogService aopLogService;
|
private final AopLogService aopLogService;
|
||||||
@@ -137,6 +144,7 @@ public class LoggingAspect {
|
|||||||
HttpServletRequest request = attributes.getRequest();
|
HttpServletRequest request = attributes.getRequest();
|
||||||
aopLog.setIpAddress(getClientIpAddress(request));
|
aopLog.setIpAddress(getClientIpAddress(request));
|
||||||
aopLog.setUserAgent(request.getHeader("User-Agent"));
|
aopLog.setUserAgent(request.getHeader("User-Agent"));
|
||||||
|
aopLog.setCurl(generateCurlCommand(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getClientIpAddress(HttpServletRequest request) {
|
private String getClientIpAddress(HttpServletRequest request) {
|
||||||
@@ -179,4 +187,132 @@ public class LoggingAspect {
|
|||||||
return e.getMessage();
|
return e.getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String generateCurlCommand(HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
StringBuilder curl = new StringBuilder("curl -X ");
|
||||||
|
|
||||||
|
curl.append(request.getMethod());
|
||||||
|
|
||||||
|
String url = getFullRequestUrl(request);
|
||||||
|
curl.append(" '").append(url).append("'");
|
||||||
|
|
||||||
|
appendHeaders(curl, request);
|
||||||
|
|
||||||
|
if (hasRequestBody(request.getMethod())) {
|
||||||
|
appendRequestBody(curl, request);
|
||||||
|
}
|
||||||
|
return curl.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to generate curl command", e);
|
||||||
|
return "curl command generation failed: " + e.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFullRequestUrl(HttpServletRequest request) {
|
||||||
|
StringBuilder url = new StringBuilder();
|
||||||
|
|
||||||
|
String scheme = request.getScheme();
|
||||||
|
String serverName = request.getServerName();
|
||||||
|
int serverPort = request.getServerPort();
|
||||||
|
|
||||||
|
if (scheme == null) {
|
||||||
|
scheme = "http";
|
||||||
|
}
|
||||||
|
if (serverName == null) {
|
||||||
|
serverName = "localhost";
|
||||||
|
}
|
||||||
|
|
||||||
|
url.append(scheme).append("://").append(serverName);
|
||||||
|
|
||||||
|
if ((scheme.equals("http") && serverPort != 80)
|
||||||
|
|| (scheme.equals("https") && serverPort != 443)) {
|
||||||
|
url.append(":").append(serverPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
url.append(request.getRequestURI());
|
||||||
|
if (request.getQueryString() != null) {
|
||||||
|
url.append("?").append(request.getQueryString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendHeaders(StringBuilder curl, HttpServletRequest request) {
|
||||||
|
Enumeration<String> headerNames = request.getHeaderNames();
|
||||||
|
for (String headerName : Collections.list(headerNames)) {
|
||||||
|
if (shouldSkipHeader(headerName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String headerValue = request.getHeader(headerName);
|
||||||
|
curl.append(" -H '").append(headerName).append(": ").append(headerValue).append("'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldSkipHeader(String headerName) {
|
||||||
|
String lowerName = headerName.toLowerCase();
|
||||||
|
return lowerName.equals("host")
|
||||||
|
|| lowerName.equals("content-length")
|
||||||
|
|| lowerName.equals("connection")
|
||||||
|
|| lowerName.startsWith("sec-")
|
||||||
|
|| lowerName.equals("upgrade-insecure-requests");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasRequestBody(String method) {
|
||||||
|
return "POST".equalsIgnoreCase(method)
|
||||||
|
|| "PUT".equalsIgnoreCase(method)
|
||||||
|
|| "PATCH".equalsIgnoreCase(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendRequestBody(StringBuilder curl, HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
String contentType = request.getContentType();
|
||||||
|
if (StringUtils.contains(contentType, "application/json")) {
|
||||||
|
String body = getRequestBody(request);
|
||||||
|
if (StringUtils.isNotEmpty(body)) {
|
||||||
|
curl.append(" -d '").append(body.replace("'", "\\'")).append("'");
|
||||||
|
}
|
||||||
|
} else if (StringUtils.contains(contentType, "application/x-www-form-urlencoded")) {
|
||||||
|
appendFormData(curl, request);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Failed to append request body to curl command", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRequestBody(HttpServletRequest request) {
|
||||||
|
try (BufferedReader reader = request.getReader()) {
|
||||||
|
if (reader == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
StringBuilder body = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
body.append(line);
|
||||||
|
}
|
||||||
|
return body.toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Failed to read request body", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendFormData(StringBuilder curl, HttpServletRequest request) {
|
||||||
|
Enumeration<String> paramNames = request.getParameterNames();
|
||||||
|
StringBuilder formData = new StringBuilder();
|
||||||
|
while (paramNames.hasMoreElements()) {
|
||||||
|
String paramName = paramNames.nextElement();
|
||||||
|
String[] paramValues = request.getParameterValues(paramName);
|
||||||
|
for (String paramValue : paramValues) {
|
||||||
|
if (!formData.isEmpty()) {
|
||||||
|
formData.append("&");
|
||||||
|
}
|
||||||
|
formData.append(paramName).append("=").append(paramValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!formData.isEmpty()) {
|
||||||
|
curl.append(" -d '").append(formData).append("'");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ public class AopLogRespDto {
|
|||||||
/** 用户代理 */
|
/** 用户代理 */
|
||||||
private String userAgent;
|
private String userAgent;
|
||||||
|
|
||||||
|
/** curl命令 */
|
||||||
|
private String curl;
|
||||||
|
|
||||||
/** 创建时间 */
|
/** 创建时间 */
|
||||||
private OffsetDateTime createTime;
|
private OffsetDateTime createTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,4 +41,7 @@ minio:
|
|||||||
endpoint: ${MINIO_ENDPOINT}
|
endpoint: ${MINIO_ENDPOINT}
|
||||||
access-key: ${MINIO_ROOT_USER}
|
access-key: ${MINIO_ROOT_USER}
|
||||||
secret-key: ${MINIO_ROOT_PASSWORD}
|
secret-key: ${MINIO_ROOT_PASSWORD}
|
||||||
default-bucket: ${MINIO_DEFAULT_BUCKETS}
|
default-bucket: ${MINIO_DEFAULT_BUCKETS}
|
||||||
|
aop:
|
||||||
|
logging:
|
||||||
|
enabled: true
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ CREATE TABLE mjga.aop_log (
|
|||||||
user_id BIGINT,
|
user_id BIGINT,
|
||||||
ip_address VARCHAR,
|
ip_address VARCHAR,
|
||||||
user_agent VARCHAR,
|
user_agent VARCHAR,
|
||||||
|
curl VARCHAR,
|
||||||
create_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
create_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (user_id) REFERENCES mjga.user(id) ON DELETE SET NULL
|
FOREIGN KEY (user_id) REFERENCES mjga.user(id) ON DELETE SET NULL
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.junit.jupiter.api.BeforeEach;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockedStatic;
|
import org.mockito.MockedStatic;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
@@ -47,11 +48,10 @@ class LoggingAspectTest {
|
|||||||
@Mock private ServletRequestAttributes servletRequestAttributes;
|
@Mock private ServletRequestAttributes servletRequestAttributes;
|
||||||
@Mock private HttpServletRequest httpServletRequest;
|
@Mock private HttpServletRequest httpServletRequest;
|
||||||
|
|
||||||
private LoggingAspect loggingAspect;
|
@InjectMocks LoggingAspect loggingAspect;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
loggingAspect = new LoggingAspect(aopLogService, objectMapper, userRepository);
|
|
||||||
SecurityContextHolder.setContext(securityContext);
|
SecurityContextHolder.setContext(securityContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +149,6 @@ class LoggingAspectTest {
|
|||||||
assertThat(log.getMethodName()).isEqualTo("serviceMethod");
|
assertThat(log.getMethodName()).isEqualTo("serviceMethod");
|
||||||
assertThat(log.getSuccess()).isTrue();
|
assertThat(log.getSuccess()).isTrue();
|
||||||
assertThat(log.getUserId()).isEqualTo(123L);
|
assertThat(log.getUserId()).isEqualTo(123L);
|
||||||
// Service层不应该有请求信息
|
|
||||||
assertThat(log.getIpAddress()).isNull();
|
assertThat(log.getIpAddress()).isNull();
|
||||||
assertThat(log.getUserAgent()).isNull();
|
assertThat(log.getUserAgent()).isNull();
|
||||||
});
|
});
|
||||||
@@ -179,7 +178,6 @@ class LoggingAspectTest {
|
|||||||
assertThat(log.getMethodName()).isEqualTo("findById");
|
assertThat(log.getMethodName()).isEqualTo("findById");
|
||||||
assertThat(log.getSuccess()).isTrue();
|
assertThat(log.getSuccess()).isTrue();
|
||||||
assertThat(log.getUserId()).isEqualTo(123L);
|
assertThat(log.getUserId()).isEqualTo(123L);
|
||||||
// Repository层不应该有请求信息
|
|
||||||
assertThat(log.getIpAddress()).isNull();
|
assertThat(log.getIpAddress()).isNull();
|
||||||
assertThat(log.getUserAgent()).isNull();
|
assertThat(log.getUserAgent()).isNull();
|
||||||
});
|
});
|
||||||
@@ -414,6 +412,51 @@ class LoggingAspectTest {
|
|||||||
return TestController.class.getMethod("skipLogMethod");
|
return TestController.class.getMethod("skipLogMethod");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void logController_givenHttpRequest_shouldGenerateCurlCommand() throws Throwable {
|
||||||
|
// arrange
|
||||||
|
TestController target = new TestController();
|
||||||
|
Object[] args = {"arg1"};
|
||||||
|
String expectedResult = "success";
|
||||||
|
User mockUser = createMockUser(123L, "testUser");
|
||||||
|
|
||||||
|
setupAuthenticatedUser("testUser", mockUser);
|
||||||
|
setupJoinPoint(target, "testMethod", args, expectedResult);
|
||||||
|
setupSerialization("[\"arg1\"]", "\"success\"");
|
||||||
|
|
||||||
|
// Setup HTTP request mocks before setupRequestContext
|
||||||
|
when(httpServletRequest.getMethod()).thenReturn("POST");
|
||||||
|
when(httpServletRequest.getScheme()).thenReturn("http");
|
||||||
|
when(httpServletRequest.getServerName()).thenReturn("localhost");
|
||||||
|
when(httpServletRequest.getServerPort()).thenReturn(8080);
|
||||||
|
when(httpServletRequest.getRequestURI()).thenReturn("/api/test");
|
||||||
|
when(httpServletRequest.getQueryString()).thenReturn("param1=value1");
|
||||||
|
when(httpServletRequest.getContentType()).thenReturn("application/json");
|
||||||
|
when(httpServletRequest.getHeaderNames())
|
||||||
|
.thenReturn(
|
||||||
|
java.util.Collections.enumeration(
|
||||||
|
java.util.Arrays.asList("Content-Type", "Authorization")));
|
||||||
|
when(httpServletRequest.getHeader("Content-Type")).thenReturn("application/json");
|
||||||
|
when(httpServletRequest.getHeader("Authorization")).thenReturn("Bearer token123");
|
||||||
|
|
||||||
|
try (MockedStatic<RequestContextHolder> mockedRequestContextHolder =
|
||||||
|
setupRequestContext("127.0.0.1", "Test-Agent")) {
|
||||||
|
// action
|
||||||
|
Object result = loggingAspect.logController(joinPoint);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
assertThat(result).isEqualTo(expectedResult);
|
||||||
|
verifyLogSaved(
|
||||||
|
log -> {
|
||||||
|
assertThat(log.getCurl()).isNotNull();
|
||||||
|
assertThat(log.getCurl()).contains("curl -X POST");
|
||||||
|
assertThat(log.getCurl()).contains("'http://localhost:8080/api/test?param1=value1'");
|
||||||
|
assertThat(log.getCurl()).contains("-H 'Content-Type: application/json'");
|
||||||
|
assertThat(log.getCurl()).contains("-H 'Authorization: Bearer token123'");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test classes for mocking
|
// Test classes for mocking
|
||||||
private static class TestController {
|
private static class TestController {
|
||||||
public String testMethod() {
|
public String testMethod() {
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ public class AopLogControllerTest {
|
|||||||
.username("testUser")
|
.username("testUser")
|
||||||
.ipAddress("127.0.0.1")
|
.ipAddress("127.0.0.1")
|
||||||
.userAgent("Test Agent")
|
.userAgent("Test Agent")
|
||||||
|
.curl("curl -X GET 'http://localhost:8080/test' -H 'Content-Type: application/json'")
|
||||||
.createTime(OffsetDateTime.now())
|
.createTime(OffsetDateTime.now())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
|||||||
|
# LoggingAspect generateCurlCommand 方法单元测试
|
||||||
|
|
||||||
|
## 测试概述
|
||||||
|
|
||||||
|
本测试文件 `LoggingAspectCurlGenerationTest.java` 专门针对 `LoggingAspect` 类中的 `generateCurlCommand` 方法进行全面的单元测试,验证该方法在各种场景下生成 curl 命令的正确性。
|
||||||
|
|
||||||
|
## 测试架构
|
||||||
|
|
||||||
|
测试采用 **嵌套测试类** 的结构,按功能模块组织:
|
||||||
|
|
||||||
|
### 1. GET 请求测试 (`GetRequestTests`)
|
||||||
|
- ✅ 基本 GET 请求 - 无查询参数
|
||||||
|
- ✅ GET 请求 - 包含查询参数
|
||||||
|
- ✅ GET 请求 - HTTPS 协议
|
||||||
|
- ✅ GET 请求 - 自定义端口
|
||||||
|
|
||||||
|
### 2. POST 请求测试 (`PostRequestTests`)
|
||||||
|
- ✅ POST 请求 - JSON 请求体
|
||||||
|
- ✅ POST 请求 - 空 JSON 请求体
|
||||||
|
- ✅ POST 请求 - 包含单引号的 JSON
|
||||||
|
|
||||||
|
### 3. PUT 和 PATCH 请求测试 (`PutAndPatchRequestTests`)
|
||||||
|
- ✅ PUT 请求 - JSON 请求体
|
||||||
|
- ✅ PATCH 请求 - JSON 请求体
|
||||||
|
|
||||||
|
### 4. 表单数据请求测试 (`FormDataRequestTests`)
|
||||||
|
- ✅ POST 请求 - 表单数据
|
||||||
|
- ✅ POST 请求 - 多值表单参数
|
||||||
|
- ✅ POST 请求 - 空表单数据
|
||||||
|
|
||||||
|
### 5. 请求头处理测试 (`HeaderProcessingTests`)
|
||||||
|
- ✅ 包含常规请求头
|
||||||
|
- ✅ 跳过特定请求头
|
||||||
|
- ✅ 验证跳过的请求头不会出现在 curl 命令中
|
||||||
|
|
||||||
|
### 6. 异常情况测试 (`ExceptionHandlingTests`)
|
||||||
|
- ✅ 读取请求体时发生 IOException
|
||||||
|
- ✅ 请求参数为 null
|
||||||
|
- ✅ 请求方法为 null
|
||||||
|
- ✅ 服务器信息为 null
|
||||||
|
|
||||||
|
### 7. 边界用例测试 (`BoundaryTests`)
|
||||||
|
- ✅ 最小化 GET 请求
|
||||||
|
- ✅ 复杂查询参数 - 包含特殊字符
|
||||||
|
- ✅ DELETE 请求 - 不应包含请求体
|
||||||
|
- ✅ HTTPS 请求 - 标准端口 443
|
||||||
|
- ✅ JSON 请求体为 null
|
||||||
|
|
||||||
|
## 测试覆盖的功能点
|
||||||
|
|
||||||
|
### 核心功能验证
|
||||||
|
1. **HTTP 方法处理**: GET, POST, PUT, PATCH, DELETE
|
||||||
|
2. **URL 构建**: 协议、主机名、端口、路径、查询参数
|
||||||
|
3. **请求头处理**: 包含/排除特定请求头
|
||||||
|
4. **请求体处理**: JSON、表单数据、空请求体
|
||||||
|
5. **异常处理**: 各种异常情况的优雅处理
|
||||||
|
|
||||||
|
### 特殊场景验证
|
||||||
|
1. **端口处理**: 标准端口省略,非标准端口包含
|
||||||
|
2. **字符转义**: JSON 中的单引号转义
|
||||||
|
3. **空值处理**: null 值的安全处理
|
||||||
|
4. **多值参数**: 表单中同名参数的多个值
|
||||||
|
|
||||||
|
## 测试技术特点
|
||||||
|
|
||||||
|
### 使用的测试技术
|
||||||
|
- **JUnit 5**: 现代化的测试框架
|
||||||
|
- **Mockito**: Mock 对象和行为验证
|
||||||
|
- **AssertJ**: 流畅的断言 API
|
||||||
|
- **嵌套测试**: 清晰的测试组织结构
|
||||||
|
|
||||||
|
### Mock 策略
|
||||||
|
- Mock `HttpServletRequest` 对象模拟各种 HTTP 请求场景
|
||||||
|
- Mock 依赖服务避免外部依赖
|
||||||
|
- 精确控制测试数据和行为
|
||||||
|
|
||||||
|
### 断言策略
|
||||||
|
- 验证生成的 curl 命令包含预期内容
|
||||||
|
- 验证不应包含的内容确实被排除
|
||||||
|
- 验证异常情况的错误消息
|
||||||
|
|
||||||
|
## 运行测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 运行所有 generateCurlCommand 相关测试
|
||||||
|
./gradlew test --tests "com.zl.mjga.unit.LoggingAspectCurlGenerationTest"
|
||||||
|
|
||||||
|
# 运行特定测试类别
|
||||||
|
./gradlew test --tests "com.zl.mjga.unit.LoggingAspectCurlGenerationTest\$GetRequestTests"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试价值
|
||||||
|
|
||||||
|
这套测试确保了 `generateCurlCommand` 方法在各种复杂场景下都能正确工作,为 AOP 日志功能的 curl 命令生成提供了可靠的质量保证。通过全面的测试覆盖,可以:
|
||||||
|
|
||||||
|
1. **防止回归**: 代码修改时及时发现问题
|
||||||
|
2. **文档作用**: 测试用例本身就是最好的使用文档
|
||||||
|
3. **重构支持**: 安全地进行代码重构
|
||||||
|
4. **质量保证**: 确保功能在各种边界条件下正常工作
|
||||||
|
|
||||||
|
## 测试结果
|
||||||
|
|
||||||
|
所有 **24 个测试用例** 均通过,覆盖了 `generateCurlCommand` 方法的所有主要功能和边界情况。
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
-- AOP日志表
|
|
||||||
CREATE TABLE mjga.aop_log (
|
CREATE TABLE mjga.aop_log (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
class_name VARCHAR NOT NULL,
|
class_name VARCHAR NOT NULL,
|
||||||
method_name VARCHAR NOT NULL,
|
method_name VARCHAR NOT NULL,
|
||||||
method_args TEXT,
|
method_args VARCHAR,
|
||||||
return_value TEXT,
|
return_value VARCHAR,
|
||||||
execution_time BIGINT NOT NULL,
|
execution_time BIGINT NOT NULL,
|
||||||
success BOOLEAN NOT NULL DEFAULT TRUE,
|
success BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
error_message TEXT,
|
error_message VARCHAR,
|
||||||
user_id BIGINT,
|
user_id BIGINT,
|
||||||
ip_address VARCHAR(45),
|
ip_address VARCHAR,
|
||||||
user_agent TEXT,
|
user_agent VARCHAR,
|
||||||
|
curl VARCHAR,
|
||||||
create_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
create_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (user_id) REFERENCES mjga.user(id) ON DELETE SET NULL
|
FOREIGN KEY (user_id) REFERENCES mjga.user(id) ON DELETE SET NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 创建索引以提高查询性能
|
|
||||||
CREATE INDEX idx_aop_log_class_name ON mjga.aop_log(class_name);
|
CREATE INDEX idx_aop_log_class_name ON mjga.aop_log(class_name);
|
||||||
CREATE INDEX idx_aop_log_method_name ON mjga.aop_log(method_name);
|
CREATE INDEX idx_aop_log_method_name ON mjga.aop_log(method_name);
|
||||||
CREATE INDEX idx_aop_log_create_time ON mjga.aop_log(create_time);
|
CREATE INDEX idx_aop_log_create_time ON mjga.aop_log(create_time);
|
||||||
|
|||||||
Reference in New Issue
Block a user