修改mybatis版本启动报错,swagger注解问题
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
package com.label;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
|
||||
class LabelBackendApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
}
|
||||
|
||||
55
src/test/java/com/label/unit/ApplicationConfigTest.java
Normal file
55
src/test/java/com/label/unit/ApplicationConfigTest.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package com.label.unit;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("应用配置单元测试")
|
||||
class ApplicationConfigTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("application.yml 提供 Swagger 和 shiro.auth 测试开关配置")
|
||||
void applicationYaml_containsSwaggerAndShiroAuthToggle() throws Exception {
|
||||
PropertySource<?> source = new YamlPropertySourceLoader()
|
||||
.load("application", new ClassPathResource("application.yml"))
|
||||
.get(0);
|
||||
|
||||
assertThat(source.getProperty("springdoc.api-docs.enabled")).isEqualTo(true);
|
||||
assertThat(source.getProperty("springdoc.api-docs.path")).isEqualTo("/v3/api-docs");
|
||||
assertThat(source.getProperty("springdoc.swagger-ui.enabled")).isEqualTo(true);
|
||||
assertThat(source.getProperty("springdoc.swagger-ui.path")).isEqualTo("/swagger-ui.html");
|
||||
assertThat(source.getProperty("shiro.auth.enabled")).isEqualTo(true);
|
||||
assertThat(source.getProperty("shiro.auth.mock-company-id")).isEqualTo(1);
|
||||
assertThat(source.getProperty("shiro.auth.mock-user-id")).isEqualTo(1);
|
||||
assertThat(source.getProperty("shiro.auth.mock-role")).isEqualTo("ADMIN");
|
||||
assertThat(source.getProperty("logging.level.com.label")).isEqualTo("INFO");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("application.yml 默认值不指向公网服务或携带真实默认密码")
|
||||
void applicationYaml_doesNotShipPublicInfrastructureDefaults() throws Exception {
|
||||
String yaml = new ClassPathResource("application.yml")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
|
||||
assertThat(yaml).doesNotContain("39.107.112.174");
|
||||
assertThat(yaml).doesNotContain("postgres!Pw");
|
||||
assertThat(yaml).doesNotContain("jsti@2024");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("logback.xml 启用 60 MB 滚动文件日志")
|
||||
void logback_enablesRollingFileAppender() throws Exception {
|
||||
String xml = new ClassPathResource("logback.xml")
|
||||
.getContentAsString(StandardCharsets.UTF_8);
|
||||
|
||||
assertThat(xml).contains("<maxFileSize>60MB</maxFileSize>");
|
||||
assertThat(xml).contains("<appender-ref ref=\"FILE\"/>");
|
||||
assertThat(xml).doesNotContain("<!-- <appender-ref ref=\"FILE\"/> -->");
|
||||
}
|
||||
}
|
||||
98
src/test/java/com/label/unit/OpenApiAnnotationTest.java
Normal file
98
src/test/java/com/label/unit/OpenApiAnnotationTest.java
Normal file
@@ -0,0 +1,98 @@
|
||||
package com.label.unit;
|
||||
|
||||
import com.label.module.annotation.controller.ExtractionController;
|
||||
import com.label.module.annotation.controller.QaController;
|
||||
import com.label.module.config.controller.SysConfigController;
|
||||
import com.label.module.export.controller.ExportController;
|
||||
import com.label.module.source.controller.SourceController;
|
||||
import com.label.module.source.dto.SourceResponse;
|
||||
import com.label.module.task.controller.TaskController;
|
||||
import com.label.module.task.dto.TaskResponse;
|
||||
import com.label.module.user.controller.AuthController;
|
||||
import com.label.module.user.controller.UserController;
|
||||
import com.label.module.user.dto.LoginRequest;
|
||||
import com.label.module.user.dto.LoginResponse;
|
||||
import com.label.module.user.dto.UserInfoResponse;
|
||||
import com.label.module.video.controller.VideoController;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("OpenAPI 注解覆盖测试")
|
||||
class OpenApiAnnotationTest {
|
||||
|
||||
private static final List<Class<?>> CONTROLLERS = List.of(
|
||||
AuthController.class,
|
||||
UserController.class,
|
||||
SourceController.class,
|
||||
TaskController.class,
|
||||
ExtractionController.class,
|
||||
QaController.class,
|
||||
ExportController.class,
|
||||
SysConfigController.class,
|
||||
VideoController.class
|
||||
);
|
||||
|
||||
private static final List<Class<?>> DTOS = List.of(
|
||||
LoginRequest.class,
|
||||
LoginResponse.class,
|
||||
UserInfoResponse.class,
|
||||
TaskResponse.class,
|
||||
SourceResponse.class
|
||||
);
|
||||
|
||||
@Test
|
||||
@DisplayName("所有 REST Controller 都声明 @Tag")
|
||||
void allControllersHaveTag() {
|
||||
assertThat(CONTROLLERS)
|
||||
.allSatisfy(controller ->
|
||||
assertThat(controller.getAnnotation(Tag.class))
|
||||
.as(controller.getSimpleName() + " should have @Tag")
|
||||
.isNotNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("所有 REST endpoint 方法都声明 @Operation")
|
||||
void allEndpointMethodsHaveOperation() {
|
||||
for (Class<?> controller : CONTROLLERS) {
|
||||
Arrays.stream(controller.getDeclaredMethods())
|
||||
.filter(method -> !Modifier.isPrivate(method.getModifiers()))
|
||||
.filter(OpenApiAnnotationTest::isEndpointMethod)
|
||||
.forEach(method -> assertThat(method.getAnnotation(Operation.class))
|
||||
.as(controller.getSimpleName() + "." + method.getName() + " should have @Operation")
|
||||
.isNotNull());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("核心 DTO 都声明 @Schema")
|
||||
void coreDtosHaveSchema() {
|
||||
assertThat(DTOS)
|
||||
.allSatisfy(dto ->
|
||||
assertThat(dto.getAnnotation(Schema.class))
|
||||
.as(dto.getSimpleName() + " should have @Schema")
|
||||
.isNotNull());
|
||||
}
|
||||
|
||||
private static boolean isEndpointMethod(Method method) {
|
||||
return method.isAnnotationPresent(GetMapping.class)
|
||||
|| method.isAnnotationPresent(PostMapping.class)
|
||||
|| method.isAnnotationPresent(PutMapping.class)
|
||||
|| method.isAnnotationPresent(DeleteMapping.class)
|
||||
|| method.isAnnotationPresent(RequestMapping.class);
|
||||
}
|
||||
}
|
||||
143
src/test/java/com/label/unit/TokenFilterTest.java
Normal file
143
src/test/java/com/label/unit/TokenFilterTest.java
Normal file
@@ -0,0 +1,143 @@
|
||||
package com.label.unit;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.label.common.context.CompanyContext;
|
||||
import com.label.common.redis.RedisKeyManager;
|
||||
import com.label.common.redis.RedisService;
|
||||
import com.label.common.shiro.BearerToken;
|
||||
import com.label.common.shiro.TokenFilter;
|
||||
import com.label.common.shiro.TokenPrincipal;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.SimpleAuthenticationInfo;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
||||
import org.apache.shiro.mgt.DefaultSecurityManager;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@DisplayName("TokenFilter 单元测试")
|
||||
class TokenFilterTest {
|
||||
|
||||
private RedisService redisService;
|
||||
private TestableTokenFilter filter;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
redisService = mock(RedisService.class);
|
||||
filter = new TestableTokenFilter(redisService, new ObjectMapper());
|
||||
SecurityUtils.setSecurityManager(new DefaultSecurityManager(new BearerTokenRealm()));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
CompanyContext.clear();
|
||||
ThreadContext.remove();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("有效 Token 请求会刷新 token TTL,实现滑动过期")
|
||||
void validToken_refreshesTokenTtl() throws Exception {
|
||||
ReflectionTestUtils.setField(filter, "authEnabled", true);
|
||||
ReflectionTestUtils.setField(filter, "tokenTtlSeconds", 7200L);
|
||||
String token = "valid-token";
|
||||
when(redisService.hGetAll(RedisKeyManager.tokenKey(token))).thenReturn(Map.of(
|
||||
"userId", "10",
|
||||
"role", "ADMIN",
|
||||
"companyId", "20",
|
||||
"username", "admin"
|
||||
));
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api/tasks");
|
||||
request.addHeader("Authorization", "Bearer " + token);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
RecordingChain chain = new RecordingChain();
|
||||
|
||||
filter.invoke(request, response, chain);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(chain.principal).isInstanceOf(TokenPrincipal.class);
|
||||
verify(redisService).expire(RedisKeyManager.tokenKey(token), 7200L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("shiro.auth.enabled=false 时注入 mock Principal 并跳过 Redis 校验")
|
||||
void authDisabled_injectsMockPrincipalWithoutRedisLookup() throws Exception {
|
||||
ReflectionTestUtils.setField(filter, "authEnabled", false);
|
||||
ReflectionTestUtils.setField(filter, "mockCompanyId", 3L);
|
||||
ReflectionTestUtils.setField(filter, "mockUserId", 4L);
|
||||
ReflectionTestUtils.setField(filter, "mockRole", "ADMIN");
|
||||
ReflectionTestUtils.setField(filter, "mockUsername", "mock-admin");
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api/tasks");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
RecordingChain chain = new RecordingChain();
|
||||
|
||||
filter.invoke(request, response, chain);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
TokenPrincipal principal = chain.principal;
|
||||
assertThat(principal.getCompanyId()).isEqualTo(3L);
|
||||
assertThat(principal.getUserId()).isEqualTo(4L);
|
||||
assertThat(principal.getRole()).isEqualTo("ADMIN");
|
||||
assertThat(principal.getUsername()).isEqualTo("mock-admin");
|
||||
verify(redisService, never()).hGetAll(anyString());
|
||||
}
|
||||
|
||||
private static final class BearerTokenRealm extends AuthorizingRealm {
|
||||
@Override
|
||||
public boolean supports(AuthenticationToken token) {
|
||||
return token instanceof BearerToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
|
||||
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
|
||||
TokenPrincipal principal = (TokenPrincipal) principals.getPrimaryPrincipal();
|
||||
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
|
||||
info.addRole(principal.getRole());
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecordingChain implements FilterChain {
|
||||
private TokenPrincipal principal;
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response) {
|
||||
principal = (TokenPrincipal) request.getAttribute("__token_principal__");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestableTokenFilter extends TokenFilter {
|
||||
private TestableTokenFilter(RedisService redisService, ObjectMapper objectMapper) {
|
||||
super(redisService, objectMapper);
|
||||
}
|
||||
|
||||
private void invoke(MockHttpServletRequest request, MockHttpServletResponse response, FilterChain chain)
|
||||
throws Exception {
|
||||
super.doFilterInternal(request, response, chain);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user