mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-09 19:03:28 +08:00
docs(zh-CN): sync Chinese docs with latest upstream changes (#304)
* docs(zh-CN): sync Chinese docs with latest upstream changes * update --------- Co-authored-by: neo <neo.dowithless@gmail.com>
This commit is contained in:
@@ -1,12 +1,21 @@
|
||||
---
|
||||
name: springboot-verification
|
||||
description: Verification loop for Spring Boot projects: build, static analysis, tests with coverage, security scans, and diff review before release or PR.
|
||||
description: "Spring Boot项目验证循环:构建、静态分析、测试覆盖、安全扫描,以及发布或PR前的差异审查。"
|
||||
origin: ECC
|
||||
---
|
||||
|
||||
# Spring Boot 验证循环
|
||||
|
||||
在提交 PR 前、重大变更后以及部署前运行。
|
||||
|
||||
## 何时激活
|
||||
|
||||
* 为 Spring Boot 服务开启拉取请求之前
|
||||
* 在重大重构或依赖项升级之后
|
||||
* 用于暂存或生产环境的部署前验证
|
||||
* 运行完整的构建 → 代码检查 → 测试 → 安全扫描流水线
|
||||
* 验证测试覆盖率是否满足阈值
|
||||
|
||||
## 阶段 1:构建
|
||||
|
||||
```bash
|
||||
@@ -45,6 +54,111 @@ mvn jacoco:report # verify 80%+ coverage
|
||||
* 总测试数,通过/失败
|
||||
* 覆盖率百分比(行/分支)
|
||||
|
||||
### 单元测试
|
||||
|
||||
使用模拟的依赖项来隔离测试服务逻辑:
|
||||
|
||||
```java
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UserServiceTest {
|
||||
|
||||
@Mock private UserRepository userRepository;
|
||||
@InjectMocks private UserService userService;
|
||||
|
||||
@Test
|
||||
void createUser_validInput_returnsUser() {
|
||||
var dto = new CreateUserDto("Alice", "alice@example.com");
|
||||
var expected = new User(1L, "Alice", "alice@example.com");
|
||||
when(userRepository.save(any(User.class))).thenReturn(expected);
|
||||
|
||||
var result = userService.create(dto);
|
||||
|
||||
assertThat(result.name()).isEqualTo("Alice");
|
||||
verify(userRepository).save(any(User.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createUser_duplicateEmail_throwsException() {
|
||||
var dto = new CreateUserDto("Alice", "existing@example.com");
|
||||
when(userRepository.existsByEmail(dto.email())).thenReturn(true);
|
||||
|
||||
assertThatThrownBy(() -> userService.create(dto))
|
||||
.isInstanceOf(DuplicateEmailException.class);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用 Testcontainers 进行集成测试
|
||||
|
||||
针对真实数据库(而非 H2)进行测试:
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
@Testcontainers
|
||||
class UserRepositoryIntegrationTest {
|
||||
|
||||
@Container
|
||||
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine")
|
||||
.withDatabaseName("testdb");
|
||||
|
||||
@DynamicPropertySource
|
||||
static void configureProperties(DynamicPropertyRegistry registry) {
|
||||
registry.add("spring.datasource.url", postgres::getJdbcUrl);
|
||||
registry.add("spring.datasource.username", postgres::getUsername);
|
||||
registry.add("spring.datasource.password", postgres::getPassword);
|
||||
}
|
||||
|
||||
@Autowired private UserRepository userRepository;
|
||||
|
||||
@Test
|
||||
void findByEmail_existingUser_returnsUser() {
|
||||
userRepository.save(new User("Alice", "alice@example.com"));
|
||||
|
||||
var found = userRepository.findByEmail("alice@example.com");
|
||||
|
||||
assertThat(found).isPresent();
|
||||
assertThat(found.get().getName()).isEqualTo("Alice");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用 MockMvc 进行 API 测试
|
||||
|
||||
在完整的 Spring 上下文中测试控制器层:
|
||||
|
||||
```java
|
||||
@WebMvcTest(UserController.class)
|
||||
class UserControllerTest {
|
||||
|
||||
@Autowired private MockMvc mockMvc;
|
||||
@MockBean private UserService userService;
|
||||
|
||||
@Test
|
||||
void createUser_validInput_returns201() throws Exception {
|
||||
var user = new UserDto(1L, "Alice", "alice@example.com");
|
||||
when(userService.create(any())).thenReturn(user);
|
||||
|
||||
mockMvc.perform(post("/api/users")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"name": "Alice", "email": "alice@example.com"}
|
||||
"""))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.name").value("Alice"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createUser_invalidEmail_returns400() throws Exception {
|
||||
mockMvc.perform(post("/api/users")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{"name": "Alice", "email": "not-an-email"}
|
||||
"""))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 阶段 4:安全扫描
|
||||
|
||||
```bash
|
||||
@@ -53,10 +167,27 @@ mvn org.owasp:dependency-check-maven:check
|
||||
# or
|
||||
./gradlew dependencyCheckAnalyze
|
||||
|
||||
# Secrets (git)
|
||||
# Secrets in source
|
||||
grep -rn "password\s*=\s*\"" src/ --include="*.java" --include="*.yml" --include="*.properties"
|
||||
grep -rn "sk-\|api_key\|secret" src/ --include="*.java" --include="*.yml"
|
||||
|
||||
# Secrets (git history)
|
||||
git secrets --scan # if configured
|
||||
```
|
||||
|
||||
### 常见安全发现
|
||||
|
||||
```
|
||||
# Check for System.out.println (use logger instead)
|
||||
grep -rn "System\.out\.print" src/main/ --include="*.java"
|
||||
|
||||
# Check for raw exception messages in responses
|
||||
grep -rn "e\.getMessage()" src/main/ --include="*.java"
|
||||
|
||||
# Check for wildcard CORS
|
||||
grep -rn "allowedOrigins.*\*" src/main/ --include="*.java"
|
||||
```
|
||||
|
||||
## 阶段 5:代码检查/格式化(可选关卡)
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user