2026-04-09 15:48:07 +08:00
|
|
|
|
package com.label.integration;
|
|
|
|
|
|
|
|
|
|
|
|
import com.label.AbstractIntegrationTest;
|
2026-04-14 13:31:50 +08:00
|
|
|
|
import com.label.module.user.dto.LoginRequest;
|
2026-04-09 15:48:07 +08:00
|
|
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
|
|
|
|
import org.junit.jupiter.api.DisplayName;
|
|
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
|
|
|
|
|
import org.springframework.http.*;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
import java.util.UUID;
|
|
|
|
|
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-04-14 13:31:50 +08:00
|
|
|
|
* 用户管理集成测试(US7)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 测试场景:
|
|
|
|
|
|
* 1. 变更角色后权限下一次请求立即生效(无需重新登录)
|
|
|
|
|
|
* 2. 禁用账号后现有 Token 下一次请求立即返回 401
|
2026-04-09 15:48:07 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
private TestRestTemplate restTemplate;
|
|
|
|
|
|
|
|
|
|
|
|
private String adminToken;
|
|
|
|
|
|
|
|
|
|
|
|
@BeforeEach
|
|
|
|
|
|
void setup() {
|
|
|
|
|
|
adminToken = loginAndGetToken("DEMO", "admin", "admin123");
|
|
|
|
|
|
assertThat(adminToken).isNotBlank();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// ------------------------------------------------------------------ 测试 1: 角色变更立即生效 --
|
2026-04-09 15:48:07 +08:00
|
|
|
|
|
|
|
|
|
|
@Test
|
2026-04-14 13:31:50 +08:00
|
|
|
|
@DisplayName("创建用户为 ANNOTATOR,变更为 REVIEWER 后同一 Token 立即可访问审批接口")
|
2026-04-09 15:48:07 +08:00
|
|
|
|
void updateRole_takesEffectImmediately() {
|
|
|
|
|
|
String uniqueUsername = "testuser-" + UUID.randomUUID().toString().substring(0, 8);
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// 1. 创建 ANNOTATOR 用户
|
2026-04-09 15:48:07 +08:00
|
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
|
|
|
headers.set("Authorization", "Bearer " + adminToken);
|
|
|
|
|
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
|
|
|
|
|
|
|
|
|
|
ResponseEntity<Map> createResp = restTemplate.exchange(
|
|
|
|
|
|
baseUrl("/api/users"),
|
|
|
|
|
|
HttpMethod.POST,
|
|
|
|
|
|
new HttpEntity<>(Map.of(
|
|
|
|
|
|
"username", uniqueUsername,
|
|
|
|
|
|
"password", "test1234",
|
2026-04-14 13:31:50 +08:00
|
|
|
|
"realName", "测试用户",
|
2026-04-09 15:48:07 +08:00
|
|
|
|
"role", "ANNOTATOR"
|
|
|
|
|
|
), headers),
|
|
|
|
|
|
Map.class);
|
|
|
|
|
|
assertThat(createResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
|
Map<String, Object> userData = (Map<String, Object>) createResp.getBody().get("data");
|
|
|
|
|
|
Long newUserId = ((Number) userData.get("id")).longValue();
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// 2. 新用户登录获取 Token
|
2026-04-09 15:48:07 +08:00
|
|
|
|
String userToken = loginAndGetToken("DEMO", uniqueUsername, "test1234");
|
|
|
|
|
|
assertThat(userToken).isNotBlank();
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// 3. 验证:ANNOTATOR 无法访问待审批队列(REVIEWER 专属)→ 403
|
2026-04-09 15:48:07 +08:00
|
|
|
|
ResponseEntity<Map> beforeRoleChange = restTemplate.exchange(
|
|
|
|
|
|
baseUrl("/api/tasks/pending-review"),
|
|
|
|
|
|
HttpMethod.GET,
|
|
|
|
|
|
bearerRequest(userToken),
|
|
|
|
|
|
Map.class);
|
|
|
|
|
|
assertThat(beforeRoleChange.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// 4. ADMIN 变更角色为 REVIEWER
|
2026-04-09 15:48:07 +08:00
|
|
|
|
ResponseEntity<Map> roleResp = restTemplate.exchange(
|
|
|
|
|
|
baseUrl("/api/users/" + newUserId + "/role"),
|
|
|
|
|
|
HttpMethod.PUT,
|
|
|
|
|
|
new HttpEntity<>(Map.of("role", "REVIEWER"), headers),
|
|
|
|
|
|
Map.class);
|
|
|
|
|
|
assertThat(roleResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// 5. 验证:同一 Token 下次请求立即具有 REVIEWER 权限 → 200
|
2026-04-09 15:48:07 +08:00
|
|
|
|
ResponseEntity<Map> afterRoleChange = restTemplate.exchange(
|
|
|
|
|
|
baseUrl("/api/tasks/pending-review"),
|
|
|
|
|
|
HttpMethod.GET,
|
|
|
|
|
|
bearerRequest(userToken),
|
|
|
|
|
|
Map.class);
|
|
|
|
|
|
assertThat(afterRoleChange.getStatusCode())
|
2026-04-14 13:31:50 +08:00
|
|
|
|
.as("角色变更后同一 Token 应立即具有 REVIEWER 权限")
|
2026-04-09 15:48:07 +08:00
|
|
|
|
.isEqualTo(HttpStatus.OK);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// ------------------------------------------------------------------ 测试 2: 禁用账号 Token 立即失效 --
|
2026-04-09 15:48:07 +08:00
|
|
|
|
|
|
|
|
|
|
@Test
|
2026-04-14 13:31:50 +08:00
|
|
|
|
@DisplayName("禁用账号后,现有 Token 下一次请求立即返回 401")
|
2026-04-09 15:48:07 +08:00
|
|
|
|
void disableAccount_tokenInvalidatedImmediately() {
|
|
|
|
|
|
String uniqueUsername = "testuser-" + UUID.randomUUID().toString().substring(0, 8);
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// 1. 创建用户
|
2026-04-09 15:48:07 +08:00
|
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
|
|
|
headers.set("Authorization", "Bearer " + adminToken);
|
|
|
|
|
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
|
|
|
|
|
|
|
|
|
|
|
ResponseEntity<Map> createResp = restTemplate.exchange(
|
|
|
|
|
|
baseUrl("/api/users"),
|
|
|
|
|
|
HttpMethod.POST,
|
|
|
|
|
|
new HttpEntity<>(Map.of(
|
|
|
|
|
|
"username", uniqueUsername,
|
|
|
|
|
|
"password", "test1234",
|
2026-04-14 13:31:50 +08:00
|
|
|
|
"realName", "测试用户",
|
2026-04-09 15:48:07 +08:00
|
|
|
|
"role", "ANNOTATOR"
|
|
|
|
|
|
), headers),
|
|
|
|
|
|
Map.class);
|
|
|
|
|
|
assertThat(createResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
|
Map<String, Object> userData = (Map<String, Object>) createResp.getBody().get("data");
|
|
|
|
|
|
Long newUserId = ((Number) userData.get("id")).longValue();
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// 2. 新用户登录,获取 Token
|
2026-04-09 15:48:07 +08:00
|
|
|
|
String userToken = loginAndGetToken("DEMO", uniqueUsername, "test1234");
|
|
|
|
|
|
assertThat(userToken).isNotBlank();
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// 3. 验证 Token 有效
|
2026-04-09 15:48:07 +08:00
|
|
|
|
ResponseEntity<Map> meResp = restTemplate.exchange(
|
|
|
|
|
|
baseUrl("/api/auth/me"),
|
|
|
|
|
|
HttpMethod.GET,
|
|
|
|
|
|
bearerRequest(userToken),
|
|
|
|
|
|
Map.class);
|
|
|
|
|
|
assertThat(meResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// 4. ADMIN 禁用账号
|
2026-04-09 15:48:07 +08:00
|
|
|
|
ResponseEntity<Map> disableResp = restTemplate.exchange(
|
|
|
|
|
|
baseUrl("/api/users/" + newUserId + "/status"),
|
|
|
|
|
|
HttpMethod.PUT,
|
|
|
|
|
|
new HttpEntity<>(Map.of("status", "DISABLED"), headers),
|
|
|
|
|
|
Map.class);
|
|
|
|
|
|
assertThat(disableResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// 5. 验证:禁用后,现有 Token 立即失效 → 401
|
2026-04-09 15:48:07 +08:00
|
|
|
|
ResponseEntity<Map> meAfterDisable = restTemplate.exchange(
|
|
|
|
|
|
baseUrl("/api/auth/me"),
|
|
|
|
|
|
HttpMethod.GET,
|
|
|
|
|
|
bearerRequest(userToken),
|
|
|
|
|
|
Map.class);
|
|
|
|
|
|
assertThat(meAfterDisable.getStatusCode())
|
2026-04-14 13:31:50 +08:00
|
|
|
|
.as("禁用账号后现有 Token 应立即失效")
|
2026-04-09 15:48:07 +08:00
|
|
|
|
.isEqualTo(HttpStatus.UNAUTHORIZED);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-14 13:31:50 +08:00
|
|
|
|
// ------------------------------------------------------------------ 工具方法 --
|
2026-04-09 15:48:07 +08:00
|
|
|
|
|
|
|
|
|
|
private String loginAndGetToken(String companyCode, String username, String password) {
|
|
|
|
|
|
LoginRequest req = new LoginRequest();
|
|
|
|
|
|
req.setCompanyCode(companyCode);
|
|
|
|
|
|
req.setUsername(username);
|
|
|
|
|
|
req.setPassword(password);
|
|
|
|
|
|
ResponseEntity<Map> response = restTemplate.postForEntity(
|
|
|
|
|
|
baseUrl("/api/auth/login"), req, Map.class);
|
|
|
|
|
|
if (!response.getStatusCode().is2xxSuccessful()) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
|
Map<String, Object> data = (Map<String, Object>) response.getBody().get("data");
|
|
|
|
|
|
return (String) data.get("token");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private HttpEntity<Void> bearerRequest(String token) {
|
|
|
|
|
|
HttpHeaders headers = new HttpHeaders();
|
|
|
|
|
|
headers.set("Authorization", "Bearer " + token);
|
|
|
|
|
|
return new HttpEntity<>(headers);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|