Revert "refactor: flatten dto entity and mapper packages"
This reverts commit 29766ebd28.
This commit is contained in:
@@ -2,7 +2,7 @@ package com.label.integration;
|
||||
|
||||
import com.label.AbstractIntegrationTest;
|
||||
import com.label.common.result.Result;
|
||||
import com.label.dto.LoginRequest;
|
||||
import com.label.module.user.dto.LoginRequest;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -15,22 +15,26 @@ import java.util.Map;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* 璁よ瘉娴佺▼闆嗘垚娴嬭瘯锛圲S1锛夈€? *
|
||||
* 娴嬭瘯鍦烘櫙锛? * 1. 姝g‘瀵嗙爜鐧诲綍 鈫?杩斿洖 token
|
||||
* 2. 閿欒瀵嗙爜鐧诲綍 鈫?401
|
||||
* 3. 涓嶅瓨鍦ㄧ殑鍏徃浠g爜 鈫?401
|
||||
* 4. 鏈夋晥 Token 璁块棶 /api/auth/me 鈫?200锛岃繑鍥炵敤鎴蜂俊鎭? * 5. 涓诲姩閫€鍑哄悗锛屽師 Token 璁块棶 /api/auth/me 鈫?401
|
||||
* 认证流程集成测试(US1)。
|
||||
*
|
||||
* 娴嬭瘯鏁版嵁鏉ヨ嚜 init.sql 绉嶅瓙锛圖EMO 鍏徃 / admin / admin123锛? */
|
||||
* 测试场景:
|
||||
* 1. 正确密码登录 → 返回 token
|
||||
* 2. 错误密码登录 → 401
|
||||
* 3. 不存在的公司代码 → 401
|
||||
* 4. 有效 Token 访问 /api/auth/me → 200,返回用户信息
|
||||
* 5. 主动退出后,原 Token 访问 /api/auth/me → 401
|
||||
*
|
||||
* 测试数据来自 init.sql 种子(DEMO 公司 / admin / admin123)
|
||||
*/
|
||||
public class AuthIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
// ------------------------------------------------------------------ 鐧诲綍娴嬭瘯 --
|
||||
// ------------------------------------------------------------------ 登录测试 --
|
||||
|
||||
@Test
|
||||
@DisplayName("姝g‘瀵嗙爜鐧诲綍 鈫?杩斿洖 token")
|
||||
@DisplayName("正确密码登录 → 返回 token")
|
||||
void login_withCorrectCredentials_returnsToken() {
|
||||
ResponseEntity<Map> response = doLogin("DEMO", "admin", "admin123");
|
||||
|
||||
@@ -48,23 +52,23 @@ public class AuthIntegrationTest extends AbstractIntegrationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("閿欒瀵嗙爜鐧诲綍 鈫?401 Unauthorized")
|
||||
@DisplayName("错误密码登录 → 401 Unauthorized")
|
||||
void login_withWrongPassword_returns401() {
|
||||
ResponseEntity<Map> response = doLogin("DEMO", "admin", "wrong_password");
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("涓嶅瓨鍦ㄧ殑鍏徃浠g爜 鈫?401 Unauthorized")
|
||||
@DisplayName("不存在的公司代码 → 401 Unauthorized")
|
||||
void login_withUnknownCompany_returns401() {
|
||||
ResponseEntity<Map> response = doLogin("NONEXIST", "admin", "admin123");
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ /me 娴嬭瘯 --
|
||||
// ------------------------------------------------------------------ /me 测试 --
|
||||
|
||||
@Test
|
||||
@DisplayName("鏈夋晥 Token 璁块棶 /api/auth/me 鈫?200锛岃繑鍥炵敤鎴蜂俊鎭?)
|
||||
@DisplayName("有效 Token 访问 /api/auth/me → 200,返回用户信息")
|
||||
void me_withValidToken_returns200WithUserInfo() {
|
||||
String token = loginAndGetToken("DEMO", "admin", "admin123");
|
||||
assertThat(token).isNotBlank();
|
||||
@@ -85,22 +89,22 @@ public class AuthIntegrationTest extends AbstractIntegrationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("鏃?Token 璁块棶 /api/auth/me 鈫?401")
|
||||
@DisplayName("无 Token 访问 /api/auth/me → 401")
|
||||
void me_withNoToken_returns401() {
|
||||
ResponseEntity<String> response = restTemplate.getForEntity(
|
||||
baseUrl("/api/auth/me"), String.class);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 閫€鍑烘祴璇?--
|
||||
// ------------------------------------------------------------------ 退出测试 --
|
||||
|
||||
@Test
|
||||
@DisplayName("涓诲姩閫€鍑哄悗锛屽師 Token 璁块棶 /api/auth/me 鈫?401")
|
||||
@DisplayName("主动退出后,原 Token 访问 /api/auth/me → 401")
|
||||
void logout_thenMe_returns401() {
|
||||
String token = loginAndGetToken("DEMO", "admin", "admin123");
|
||||
assertThat(token).isNotBlank();
|
||||
|
||||
// 纭鐧诲綍鏈夋晥
|
||||
// 确认登录有效
|
||||
ResponseEntity<Map> meResponse = restTemplate.exchange(
|
||||
baseUrl("/api/auth/me"),
|
||||
HttpMethod.GET,
|
||||
@@ -108,14 +112,15 @@ public class AuthIntegrationTest extends AbstractIntegrationTest {
|
||||
Map.class);
|
||||
assertThat(meResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 閫€鍑? ResponseEntity<Map> logoutResponse = restTemplate.exchange(
|
||||
// 退出
|
||||
ResponseEntity<Map> logoutResponse = restTemplate.exchange(
|
||||
baseUrl("/api/auth/logout"),
|
||||
HttpMethod.POST,
|
||||
bearerRequest(token),
|
||||
Map.class);
|
||||
assertThat(logoutResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 閫€鍑哄悗鍐嶈闂?/me 鈫?401
|
||||
// 退出后再访问 /me → 401
|
||||
ResponseEntity<Map> meAfterLogout = restTemplate.exchange(
|
||||
baseUrl("/api/auth/me"),
|
||||
HttpMethod.GET,
|
||||
@@ -124,9 +129,9 @@ public class AuthIntegrationTest extends AbstractIntegrationTest {
|
||||
assertThat(meAfterLogout.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 宸ュ叿鏂规硶 --
|
||||
// ------------------------------------------------------------------ 工具方法 --
|
||||
|
||||
/** 鍙戣捣鐧诲綍璇锋眰锛岃繑鍥炲師濮?ResponseEntity */
|
||||
/** 发起登录请求,返回原始 ResponseEntity */
|
||||
private ResponseEntity<Map> doLogin(String companyCode, String username, String password) {
|
||||
LoginRequest req = new LoginRequest();
|
||||
req.setCompanyCode(companyCode);
|
||||
@@ -135,7 +140,7 @@ public class AuthIntegrationTest extends AbstractIntegrationTest {
|
||||
return restTemplate.postForEntity(baseUrl("/api/auth/login"), req, Map.class);
|
||||
}
|
||||
|
||||
/** 鐧诲綍骞舵彁鍙?token 瀛楃涓诧紱澶辫触鏃惰繑鍥?null */
|
||||
/** 登录并提取 token 字符串;失败时返回 null */
|
||||
private String loginAndGetToken(String companyCode, String username, String password) {
|
||||
ResponseEntity<Map> response = doLogin(companyCode, username, password);
|
||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
||||
@@ -146,7 +151,7 @@ public class AuthIntegrationTest extends AbstractIntegrationTest {
|
||||
return (String) data.get("token");
|
||||
}
|
||||
|
||||
/** 鏋勯€犲甫 Bearer Token 鐨勮姹傚疄浣擄紙鏃?body锛?*/
|
||||
/** 构造带 Bearer Token 的请求实体(无 body) */
|
||||
private HttpEntity<Void> bearerRequest(String token) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + token);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.label.integration;
|
||||
|
||||
import com.label.AbstractIntegrationTest;
|
||||
import com.label.dto.LoginRequest;
|
||||
import com.label.module.user.dto.LoginRequest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -14,10 +14,12 @@ import java.util.Map;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* 鎻愬彇闃舵瀹℃壒闆嗘垚娴嬭瘯锛圲S4锛夈€? *
|
||||
* 娴嬭瘯鍦烘櫙锛? * 1. 瀹℃壒閫氳繃 鈫?QA_GENERATION 浠诲姟鑷姩鍒涘缓锛宻ource_data 鐘舵€佹洿鏂颁负 QA_REVIEW
|
||||
* 2. 瀹℃壒浜轰笌鎻愪氦浜虹浉鍚岋紙鑷锛夆啋 403 SELF_REVIEW_FORBIDDEN
|
||||
* 3. 椹冲洖鍚庢爣娉ㄥ憳鍙噸棰嗕换鍔″苟鍐嶆鎻愪氦
|
||||
* 提取阶段审批集成测试(US4)。
|
||||
*
|
||||
* 测试场景:
|
||||
* 1. 审批通过 → QA_GENERATION 任务自动创建,source_data 状态更新为 QA_REVIEW
|
||||
* 2. 审批人与提交人相同(自审)→ 403 SELF_REVIEW_FORBIDDEN
|
||||
* 3. 驳回后标注员可重领任务并再次提交
|
||||
*/
|
||||
public class ExtractionApprovalIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
@@ -31,14 +33,15 @@ public class ExtractionApprovalIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
// 鑾峰彇绉嶅瓙鐢ㄦ埛 ID锛坕nit.sql 涓凡鎻掑叆锛? annotatorUserId = jdbcTemplate.queryForObject(
|
||||
// 获取种子用户 ID(init.sql 中已插入)
|
||||
annotatorUserId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM sys_user WHERE username = 'annotator01'", Long.class);
|
||||
reviewerUserId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM sys_user WHERE username = 'reviewer01'", Long.class);
|
||||
Long companyId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM sys_company WHERE company_code = 'DEMO'", Long.class);
|
||||
|
||||
// 鎻掑叆娴嬭瘯 source_data
|
||||
// 插入测试 source_data
|
||||
jdbcTemplate.execute(
|
||||
"INSERT INTO source_data (company_id, uploader_id, data_type, file_path, " +
|
||||
"file_name, file_size, bucket_name, status) " +
|
||||
@@ -47,7 +50,7 @@ public class ExtractionApprovalIntegrationTest extends AbstractIntegrationTest {
|
||||
sourceId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM source_data ORDER BY id DESC LIMIT 1", Long.class);
|
||||
|
||||
// 鎻掑叆 UNCLAIMED EXTRACTION 浠诲姟
|
||||
// 插入 UNCLAIMED EXTRACTION 任务
|
||||
jdbcTemplate.execute(
|
||||
"INSERT INTO annotation_task (company_id, source_id, task_type, status) " +
|
||||
"VALUES (" + companyId + ", " + sourceId + ", 'EXTRACTION', 'UNCLAIMED')");
|
||||
@@ -55,63 +58,66 @@ public class ExtractionApprovalIntegrationTest extends AbstractIntegrationTest {
|
||||
"SELECT id FROM annotation_task ORDER BY id DESC LIMIT 1", Long.class);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 娴嬭瘯 1: 瀹℃壒閫氳繃 鈫?QA 浠诲姟鑷姩鍒涘缓 --
|
||||
// ------------------------------------------------------------------ 测试 1: 审批通过 → QA 任务自动创建 --
|
||||
|
||||
@Test
|
||||
@DisplayName("瀹℃壒閫氳繃鍚庯紝QA_GENERATION 浠诲姟鑷姩鍒涘缓锛宻ource_data 鐘舵€佸彉涓?QA_REVIEW")
|
||||
@DisplayName("审批通过后,QA_GENERATION 任务自动创建,source_data 状态变为 QA_REVIEW")
|
||||
void approveTask_thenQaTaskAndSourceStatusUpdated() {
|
||||
String annotatorToken = loginAndGetToken("DEMO", "annotator01", "annot123");
|
||||
String reviewerToken = loginAndGetToken("DEMO", "reviewer01", "review123");
|
||||
|
||||
// 1. 鏍囨敞鍛橀鍙栦换鍔? ResponseEntity<Map> claimResp = restTemplate.exchange(
|
||||
// 1. 标注员领取任务
|
||||
ResponseEntity<Map> claimResp = restTemplate.exchange(
|
||||
baseUrl("/api/tasks/" + taskId + "/claim"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
assertThat(claimResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 2. 鏍囨敞鍛樻彁浜ゆ爣娉? ResponseEntity<Map> submitResp = restTemplate.exchange(
|
||||
// 2. 标注员提交标注
|
||||
ResponseEntity<Map> submitResp = restTemplate.exchange(
|
||||
baseUrl("/api/extraction/" + taskId + "/submit"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
assertThat(submitResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 3. 瀹℃牳鍛樺鎵归€氳繃
|
||||
// 娉細ExtractionApprovedEventListener(@TransactionalEventListener AFTER_COMMIT)
|
||||
// 鍦ㄥ悓涓€绾跨▼涓悓姝ユ墽琛岋紝HTTP 鍝嶅簲杩斿洖鍓嶅凡瀹屾垚鍚庣画澶勭悊
|
||||
// 3. 审核员审批通过
|
||||
// 注:ExtractionApprovedEventListener(@TransactionalEventListener AFTER_COMMIT)
|
||||
// 在同一线程中同步执行,HTTP 响应返回前已完成后续处理
|
||||
ResponseEntity<Map> approveResp = restTemplate.exchange(
|
||||
baseUrl("/api/extraction/" + taskId + "/approve"),
|
||||
HttpMethod.POST, bearerRequest(reviewerToken), Map.class);
|
||||
assertThat(approveResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 楠岃瘉锛氬師浠诲姟鐘舵€佸彉涓?APPROVED锛宨s_final=true
|
||||
// 验证:原任务状态变为 APPROVED,is_final=true
|
||||
Map<String, Object> taskRow = jdbcTemplate.queryForMap(
|
||||
"SELECT status, is_final FROM annotation_task WHERE id = ?", taskId);
|
||||
assertThat(taskRow.get("status")).isEqualTo("APPROVED");
|
||||
assertThat(taskRow.get("is_final")).isEqualTo(Boolean.TRUE);
|
||||
|
||||
// 楠岃瘉锛歈A_GENERATION 浠诲姟宸茶嚜鍔ㄥ垱寤猴紙UNCLAIMED 鐘舵€侊級
|
||||
// 验证:QA_GENERATION 任务已自动创建(UNCLAIMED 状态)
|
||||
Integer qaTaskCount = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM annotation_task " +
|
||||
"WHERE source_id = ? AND task_type = 'QA_GENERATION' AND status = 'UNCLAIMED'",
|
||||
Integer.class, sourceId);
|
||||
assertThat(qaTaskCount).as("QA_GENERATION 浠诲姟搴斿凡鍒涘缓").isEqualTo(1);
|
||||
assertThat(qaTaskCount).as("QA_GENERATION 任务应已创建").isEqualTo(1);
|
||||
|
||||
// 楠岃瘉锛歴ource_data 鐘舵€佸凡鏇存柊涓?QA_REVIEW
|
||||
// 验证:source_data 状态已更新为 QA_REVIEW
|
||||
String sourceStatus = jdbcTemplate.queryForObject(
|
||||
"SELECT status FROM source_data WHERE id = ?", String.class, sourceId);
|
||||
assertThat(sourceStatus).as("source_data 鐘舵€佸簲涓?QA_REVIEW").isEqualTo("QA_REVIEW");
|
||||
assertThat(sourceStatus).as("source_data 状态应为 QA_REVIEW").isEqualTo("QA_REVIEW");
|
||||
|
||||
// 楠岃瘉锛歵raining_dataset 宸蹭互 PENDING_REVIEW 鐘舵€佸垱寤? Integer datasetCount = jdbcTemplate.queryForObject(
|
||||
// 验证:training_dataset 已以 PENDING_REVIEW 状态创建
|
||||
Integer datasetCount = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM training_dataset " +
|
||||
"WHERE source_id = ? AND status = 'PENDING_REVIEW'",
|
||||
Integer.class, sourceId);
|
||||
assertThat(datasetCount).as("training_dataset 搴斿凡鍒涘缓").isEqualTo(1);
|
||||
assertThat(datasetCount).as("training_dataset 应已创建").isEqualTo(1);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 娴嬭瘯 2: 鑷杩斿洖 403 --
|
||||
// ------------------------------------------------------------------ 测试 2: 自审返回 403 --
|
||||
|
||||
@Test
|
||||
@DisplayName("瀹℃壒浜轰笌浠诲姟棰嗗彇浜虹浉鍚岋紙鑷锛夆啋 403 SELF_REVIEW_FORBIDDEN")
|
||||
@DisplayName("审批人与任务领取人相同(自审)→ 403 SELF_REVIEW_FORBIDDEN")
|
||||
void approveOwnSubmission_returnsForbidden() {
|
||||
// 鐩存帴灏嗕换鍔$疆涓?SUBMITTED 骞惰 claimed_by = reviewer01锛堟ā鎷熻嚜瀹″満鏅級
|
||||
// 直接将任务置为 SUBMITTED 并设 claimed_by = reviewer01(模拟自审场景)
|
||||
jdbcTemplate.execute(
|
||||
"UPDATE annotation_task " +
|
||||
"SET status = 'SUBMITTED', claimed_by = " + reviewerUserId +
|
||||
@@ -126,63 +132,67 @@ public class ExtractionApprovalIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
|
||||
// 楠岃瘉浠诲姟鐘舵€佹湭鍙? String status = jdbcTemplate.queryForObject(
|
||||
// 验证任务状态未变
|
||||
String status = jdbcTemplate.queryForObject(
|
||||
"SELECT status FROM annotation_task WHERE id = ?", String.class, taskId);
|
||||
assertThat(status).isEqualTo("SUBMITTED");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 娴嬭瘯 3: 椹冲洖 鈫?閲嶉 鈫?鍐嶆彁浜?--
|
||||
// ------------------------------------------------------------------ 测试 3: 驳回 → 重领 → 再提交 --
|
||||
|
||||
@Test
|
||||
@DisplayName("椹冲洖鍚庢爣娉ㄥ憳鍙噸棰嗕换鍔″苟鍐嶆鎻愪氦锛屼换鍔$姸鎬佹仮澶嶄负 SUBMITTED")
|
||||
@DisplayName("驳回后标注员可重领任务并再次提交,任务状态恢复为 SUBMITTED")
|
||||
void rejectThenReclaimAndResubmit_succeeds() {
|
||||
String annotatorToken = loginAndGetToken("DEMO", "annotator01", "annot123");
|
||||
String reviewerToken = loginAndGetToken("DEMO", "reviewer01", "review123");
|
||||
|
||||
// 1. 鏍囨敞鍛橀鍙栧苟鎻愪氦
|
||||
// 1. 标注员领取并提交
|
||||
restTemplate.exchange(baseUrl("/api/tasks/" + taskId + "/claim"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
restTemplate.exchange(baseUrl("/api/extraction/" + taskId + "/submit"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
|
||||
// 2. 瀹℃牳鍛橀┏鍥烇紙椹冲洖鍘熷洜蹇呭~锛? HttpHeaders rejectHeaders = new HttpHeaders();
|
||||
// 2. 审核员驳回(驳回原因必填)
|
||||
HttpHeaders rejectHeaders = new HttpHeaders();
|
||||
rejectHeaders.set("Authorization", "Bearer " + reviewerToken);
|
||||
rejectHeaders.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<Map<String, String>> rejectReq = new HttpEntity<>(
|
||||
Map.of("reason", "瀹炰綋璇嗗埆鏈夎锛岃閲嶆柊鏍囨敞"), rejectHeaders);
|
||||
Map.of("reason", "实体识别有误,请重新标注"), rejectHeaders);
|
||||
|
||||
ResponseEntity<Map> rejectResp = restTemplate.exchange(
|
||||
baseUrl("/api/extraction/" + taskId + "/reject"),
|
||||
HttpMethod.POST, rejectReq, Map.class);
|
||||
assertThat(rejectResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 楠岃瘉锛氫换鍔$姸鎬佸彉涓?REJECTED
|
||||
// 验证:任务状态变为 REJECTED
|
||||
String statusAfterReject = jdbcTemplate.queryForObject(
|
||||
"SELECT status FROM annotation_task WHERE id = ?", String.class, taskId);
|
||||
assertThat(statusAfterReject).isEqualTo("REJECTED");
|
||||
|
||||
// 3. 鏍囨敞鍛橀噸棰嗕换鍔★紙REJECTED 鈫?IN_PROGRESS锛? ResponseEntity<Map> reclaimResp = restTemplate.exchange(
|
||||
// 3. 标注员重领任务(REJECTED → IN_PROGRESS)
|
||||
ResponseEntity<Map> reclaimResp = restTemplate.exchange(
|
||||
baseUrl("/api/tasks/" + taskId + "/reclaim"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
assertThat(reclaimResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 楠岃瘉锛氫换鍔$姸鎬佹仮澶嶄负 IN_PROGRESS
|
||||
// 验证:任务状态恢复为 IN_PROGRESS
|
||||
String statusAfterReclaim = jdbcTemplate.queryForObject(
|
||||
"SELECT status FROM annotation_task WHERE id = ?", String.class, taskId);
|
||||
assertThat(statusAfterReclaim).isEqualTo("IN_PROGRESS");
|
||||
|
||||
// 4. 鏍囨敞鍛樺啀娆℃彁浜わ紙IN_PROGRESS 鈫?SUBMITTED锛? ResponseEntity<Map> resubmitResp = restTemplate.exchange(
|
||||
// 4. 标注员再次提交(IN_PROGRESS → SUBMITTED)
|
||||
ResponseEntity<Map> resubmitResp = restTemplate.exchange(
|
||||
baseUrl("/api/extraction/" + taskId + "/submit"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
assertThat(resubmitResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 楠岃瘉锛氫换鍔$姸鎬佸彉涓?SUBMITTED
|
||||
// 验证:任务状态变为 SUBMITTED
|
||||
String finalStatus = jdbcTemplate.queryForObject(
|
||||
"SELECT status FROM annotation_task WHERE id = ?", String.class, taskId);
|
||||
assertThat(finalStatus).isEqualTo("SUBMITTED");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 宸ュ叿鏂规硶 --
|
||||
// ------------------------------------------------------------------ 工具方法 --
|
||||
|
||||
private String loginAndGetToken(String companyCode, String username, String password) {
|
||||
LoginRequest req = new LoginRequest();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.label.integration;
|
||||
|
||||
import com.label.AbstractIntegrationTest;
|
||||
import com.label.dto.LoginRequest;
|
||||
import com.label.module.user.dto.LoginRequest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -14,9 +14,11 @@ import java.util.Map;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* QA 闂瓟鐢熸垚闃舵瀹℃壒闆嗘垚娴嬭瘯锛圲S5锛夈€? *
|
||||
* 娴嬭瘯鍦烘櫙锛? * 1. QA 瀹℃壒閫氳繃 鈫?training_dataset.status = APPROVED锛宻ource_data.status = APPROVED
|
||||
* 2. QA 椹冲洖 鈫?鍊欓€夐棶绛斿琚垹闄わ紝鏍囨敞鍛樺彲閲嶉
|
||||
* QA 问答生成阶段审批集成测试(US5)。
|
||||
*
|
||||
* 测试场景:
|
||||
* 1. QA 审批通过 → training_dataset.status = APPROVED,source_data.status = APPROVED
|
||||
* 2. QA 驳回 → 候选问答对被删除,标注员可重领
|
||||
*/
|
||||
public class QaApprovalIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
@@ -38,7 +40,7 @@ public class QaApprovalIntegrationTest extends AbstractIntegrationTest {
|
||||
Long companyId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM sys_company WHERE company_code = 'DEMO'", Long.class);
|
||||
|
||||
// 鎻掑叆 source_data锛圦A_REVIEW 鐘舵€侊紝妯℃嫙鎻愬彇瀹℃壒宸插畬鎴愶級
|
||||
// 插入 source_data(QA_REVIEW 状态,模拟提取审批已完成)
|
||||
jdbcTemplate.execute(
|
||||
"INSERT INTO source_data (company_id, uploader_id, data_type, file_path, " +
|
||||
"file_name, file_size, bucket_name, status) " +
|
||||
@@ -47,123 +49,129 @@ public class QaApprovalIntegrationTest extends AbstractIntegrationTest {
|
||||
sourceId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM source_data ORDER BY id DESC LIMIT 1", Long.class);
|
||||
|
||||
// 鎻掑叆 QA_GENERATION 浠诲姟锛圲NCLAIMED 鐘舵€侊紝妯℃嫙鎻愬彇瀹℃壒閫氳繃鍚庤嚜鍔ㄥ垱寤虹殑 QA 浠诲姟锛? jdbcTemplate.execute(
|
||||
// 插入 QA_GENERATION 任务(UNCLAIMED 状态,模拟提取审批通过后自动创建的 QA 任务)
|
||||
jdbcTemplate.execute(
|
||||
"INSERT INTO annotation_task (company_id, source_id, task_type, status) " +
|
||||
"VALUES (" + companyId + ", " + sourceId + ", 'QA_GENERATION', 'UNCLAIMED')");
|
||||
taskId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM annotation_task ORDER BY id DESC LIMIT 1", Long.class);
|
||||
|
||||
// 鎻掑叆鍊欓€夐棶绛斿锛堟ā鎷?ExtractionApprovedEventListener 鍒涘缓锛? jdbcTemplate.execute(
|
||||
// 插入候选问答对(模拟 ExtractionApprovedEventListener 创建)
|
||||
jdbcTemplate.execute(
|
||||
"INSERT INTO training_dataset (company_id, task_id, source_id, sample_type, " +
|
||||
"glm_format_json, status) VALUES (" + companyId + ", " + taskId + ", " + sourceId +
|
||||
", 'TEXT', '{\"conversations\":[{\"question\":\"鍖椾含鏄摢涓浗瀹剁殑棣栭兘锛焅",\"answer\":\"涓浗\"}]}'::jsonb, " +
|
||||
", 'TEXT', '{\"conversations\":[{\"question\":\"北京是哪个国家的首都?\",\"answer\":\"中国\"}]}'::jsonb, " +
|
||||
"'PENDING_REVIEW')");
|
||||
datasetId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM training_dataset ORDER BY id DESC LIMIT 1", Long.class);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 娴嬭瘯 1: 瀹℃壒閫氳繃 鈫?缁堟€?--
|
||||
// ------------------------------------------------------------------ 测试 1: 审批通过 → 终态 --
|
||||
|
||||
@Test
|
||||
@DisplayName("QA 瀹℃壒閫氳繃 鈫?training_dataset.status=APPROVED锛宻ource_data.status=APPROVED")
|
||||
@DisplayName("QA 审批通过 → training_dataset.status=APPROVED,source_data.status=APPROVED")
|
||||
void approveQaTask_thenDatasetAndSourceApproved() {
|
||||
String annotatorToken = loginAndGetToken("DEMO", "annotator01", "annot123");
|
||||
String reviewerToken = loginAndGetToken("DEMO", "reviewer01", "review123");
|
||||
|
||||
// 娉ㄦ剰锛歈A 浠诲姟 claim 绔偣涓?POST /api/tasks/{id}/claim锛圓NNOTATOR 瑙掕壊锛? // 浣?TaskController.getPool 鍙粰 ANNOTATOR 鏄剧ず EXTRACTION/UNCLAIMED
|
||||
// QA 浠诲姟鐢?ANNOTATOR 鐩存帴棰嗗彇锛堜笉缁忚繃浠诲姟姹狅級
|
||||
// 注意:QA 任务 claim 端点为 POST /api/tasks/{id}/claim(ANNOTATOR 角色)
|
||||
// 但 TaskController.getPool 只给 ANNOTATOR 显示 EXTRACTION/UNCLAIMED
|
||||
// QA 任务由 ANNOTATOR 直接领取(不经过任务池)
|
||||
ResponseEntity<Map> claimResp = restTemplate.exchange(
|
||||
baseUrl("/api/tasks/" + taskId + "/claim"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
assertThat(claimResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 鎻愪氦 QA 缁撴灉
|
||||
// 提交 QA 结果
|
||||
ResponseEntity<Map> submitResp = restTemplate.exchange(
|
||||
baseUrl("/api/qa/" + taskId + "/submit"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
assertThat(submitResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 瀹℃壒閫氳繃
|
||||
// 审批通过
|
||||
ResponseEntity<Map> approveResp = restTemplate.exchange(
|
||||
baseUrl("/api/qa/" + taskId + "/approve"),
|
||||
HttpMethod.POST, bearerRequest(reviewerToken), Map.class);
|
||||
assertThat(approveResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 楠岃瘉锛歵raining_dataset 鈫?APPROVED
|
||||
// 验证:training_dataset → APPROVED
|
||||
String datasetStatus = jdbcTemplate.queryForObject(
|
||||
"SELECT status FROM training_dataset WHERE id = ?", String.class, datasetId);
|
||||
assertThat(datasetStatus).as("training_dataset 鐘舵€佸簲涓?APPROVED").isEqualTo("APPROVED");
|
||||
assertThat(datasetStatus).as("training_dataset 状态应为 APPROVED").isEqualTo("APPROVED");
|
||||
|
||||
// 楠岃瘉锛歛nnotation_task 鈫?APPROVED锛宨s_final=true
|
||||
// 验证:annotation_task → APPROVED,is_final=true
|
||||
Map<String, Object> taskRow = jdbcTemplate.queryForMap(
|
||||
"SELECT status, is_final FROM annotation_task WHERE id = ?", taskId);
|
||||
assertThat(taskRow.get("status")).isEqualTo("APPROVED");
|
||||
assertThat(taskRow.get("is_final")).isEqualTo(Boolean.TRUE);
|
||||
|
||||
// 楠岃瘉锛歴ource_data 鈫?APPROVED锛堟暣鏉℃祦姘寸嚎瀹屾垚锛? String sourceStatus = jdbcTemplate.queryForObject(
|
||||
// 验证:source_data → APPROVED(整条流水线完成)
|
||||
String sourceStatus = jdbcTemplate.queryForObject(
|
||||
"SELECT status FROM source_data WHERE id = ?", String.class, sourceId);
|
||||
assertThat(sourceStatus).as("source_data 鐘舵€佸簲涓?APPROVED锛堟祦姘寸嚎缁堟€侊級").isEqualTo("APPROVED");
|
||||
assertThat(sourceStatus).as("source_data 状态应为 APPROVED(流水线终态)").isEqualTo("APPROVED");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 娴嬭瘯 2: 椹冲洖 鈫?鍊欓€夎褰曞垹闄?鈫?鍙噸棰?--
|
||||
// ------------------------------------------------------------------ 测试 2: 驳回 → 候选记录删除 → 可重领 --
|
||||
|
||||
@Test
|
||||
@DisplayName("QA 椹冲洖 鈫?鍊欓€夐棶绛斿琚垹闄わ紝鏍囨敞鍛樺彲閲嶉骞跺啀娆℃彁浜?)
|
||||
@DisplayName("QA 驳回 → 候选问答对被删除,标注员可重领并再次提交")
|
||||
void rejectQaTask_thenDatasetDeletedAndReclaimable() {
|
||||
String annotatorToken = loginAndGetToken("DEMO", "annotator01", "annot123");
|
||||
String reviewerToken = loginAndGetToken("DEMO", "reviewer01", "review123");
|
||||
|
||||
// 棰嗗彇骞舵彁浜? restTemplate.exchange(baseUrl("/api/tasks/" + taskId + "/claim"),
|
||||
// 领取并提交
|
||||
restTemplate.exchange(baseUrl("/api/tasks/" + taskId + "/claim"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
restTemplate.exchange(baseUrl("/api/qa/" + taskId + "/submit"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
|
||||
// 椹冲洖锛堥┏鍥炲師鍥犲繀濉級
|
||||
// 驳回(驳回原因必填)
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + reviewerToken);
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<Map<String, String>> rejectReq = new HttpEntity<>(
|
||||
Map.of("reason", "闂鎻忚堪涓嶅噯纭紝璇蜂慨鏀?), headers);
|
||||
Map.of("reason", "问题描述不准确,请修改"), headers);
|
||||
|
||||
ResponseEntity<Map> rejectResp = restTemplate.exchange(
|
||||
baseUrl("/api/qa/" + taskId + "/reject"),
|
||||
HttpMethod.POST, rejectReq, Map.class);
|
||||
assertThat(rejectResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 楠岃瘉锛氫换鍔$姸鎬佸彉涓?REJECTED
|
||||
// 验证:任务状态变为 REJECTED
|
||||
String statusAfterReject = jdbcTemplate.queryForObject(
|
||||
"SELECT status FROM annotation_task WHERE id = ?", String.class, taskId);
|
||||
assertThat(statusAfterReject).isEqualTo("REJECTED");
|
||||
|
||||
// 楠岃瘉锛氬€欓€夐棶绛斿宸茶鍒犻櫎
|
||||
// 验证:候选问答对已被删除
|
||||
Integer datasetCount = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM training_dataset WHERE task_id = ?",
|
||||
Integer.class, taskId);
|
||||
assertThat(datasetCount).as("椹冲洖鍚庡€欓€夐棶绛斿搴旇鍒犻櫎").isEqualTo(0);
|
||||
assertThat(datasetCount).as("驳回后候选问答对应被删除").isEqualTo(0);
|
||||
|
||||
// 楠岃瘉锛歴ource_data 淇濇寔 QA_REVIEW锛堜笉鍙橈級
|
||||
// 验证:source_data 保持 QA_REVIEW(不变)
|
||||
String sourceStatus = jdbcTemplate.queryForObject(
|
||||
"SELECT status FROM source_data WHERE id = ?", String.class, sourceId);
|
||||
assertThat(sourceStatus).as("椹冲洖鍚?source_data 搴斾繚鎸?QA_REVIEW").isEqualTo("QA_REVIEW");
|
||||
assertThat(sourceStatus).as("驳回后 source_data 应保持 QA_REVIEW").isEqualTo("QA_REVIEW");
|
||||
|
||||
// 鏍囨敞鍛橀噸棰嗕换鍔? ResponseEntity<Map> reclaimResp = restTemplate.exchange(
|
||||
// 标注员重领任务
|
||||
ResponseEntity<Map> reclaimResp = restTemplate.exchange(
|
||||
baseUrl("/api/tasks/" + taskId + "/reclaim"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
assertThat(reclaimResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 鍐嶆鎻愪氦
|
||||
// 再次提交
|
||||
ResponseEntity<Map> resubmitResp = restTemplate.exchange(
|
||||
baseUrl("/api/qa/" + taskId + "/submit"),
|
||||
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
|
||||
assertThat(resubmitResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 楠岃瘉锛氫换鍔$姸鎬佸彉涓?SUBMITTED
|
||||
// 验证:任务状态变为 SUBMITTED
|
||||
String finalStatus = jdbcTemplate.queryForObject(
|
||||
"SELECT status FROM annotation_task WHERE id = ?", String.class, taskId);
|
||||
assertThat(finalStatus).isEqualTo("SUBMITTED");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 宸ュ叿鏂规硶 --
|
||||
// ------------------------------------------------------------------ 工具方法 --
|
||||
|
||||
private String loginAndGetToken(String companyCode, String username, String password) {
|
||||
LoginRequest req = new LoginRequest();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.label.integration;
|
||||
|
||||
import com.label.AbstractIntegrationTest;
|
||||
import com.label.dto.LoginRequest;
|
||||
import com.label.module.user.dto.LoginRequest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -15,8 +15,11 @@ import java.util.UUID;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* 鐢ㄦ埛绠$悊闆嗘垚娴嬭瘯锛圲S7锛夈€? *
|
||||
* 娴嬭瘯鍦烘櫙锛? * 1. 鍙樻洿瑙掕壊鍚庢潈闄愪笅涓€娆¤姹傜珛鍗崇敓鏁堬紙鏃犻渶閲嶆柊鐧诲綍锛? * 2. 绂佺敤璐﹀彿鍚庣幇鏈?Token 涓嬩竴娆¤姹傜珛鍗宠繑鍥?401
|
||||
* 用户管理集成测试(US7)。
|
||||
*
|
||||
* 测试场景:
|
||||
* 1. 变更角色后权限下一次请求立即生效(无需重新登录)
|
||||
* 2. 禁用账号后现有 Token 下一次请求立即返回 401
|
||||
*/
|
||||
public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
||||
|
||||
@@ -31,14 +34,14 @@ public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
||||
assertThat(adminToken).isNotBlank();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 娴嬭瘯 1: 瑙掕壊鍙樻洿绔嬪嵆鐢熸晥 --
|
||||
// ------------------------------------------------------------------ 测试 1: 角色变更立即生效 --
|
||||
|
||||
@Test
|
||||
@DisplayName("鍒涘缓鐢ㄦ埛涓?ANNOTATOR锛屽彉鏇翠负 REVIEWER 鍚庡悓涓€ Token 绔嬪嵆鍙闂鎵规帴鍙?)
|
||||
@DisplayName("创建用户为 ANNOTATOR,变更为 REVIEWER 后同一 Token 立即可访问审批接口")
|
||||
void updateRole_takesEffectImmediately() {
|
||||
String uniqueUsername = "testuser-" + UUID.randomUUID().toString().substring(0, 8);
|
||||
|
||||
// 1. 鍒涘缓 ANNOTATOR 鐢ㄦ埛
|
||||
// 1. 创建 ANNOTATOR 用户
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + adminToken);
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
@@ -49,7 +52,7 @@ public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
||||
new HttpEntity<>(Map.of(
|
||||
"username", uniqueUsername,
|
||||
"password", "test1234",
|
||||
"realName", "娴嬭瘯鐢ㄦ埛",
|
||||
"realName", "测试用户",
|
||||
"role", "ANNOTATOR"
|
||||
), headers),
|
||||
Map.class);
|
||||
@@ -59,11 +62,11 @@ public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
||||
Map<String, Object> userData = (Map<String, Object>) createResp.getBody().get("data");
|
||||
Long newUserId = ((Number) userData.get("id")).longValue();
|
||||
|
||||
// 2. 鏂扮敤鎴风櫥褰曡幏鍙?Token
|
||||
// 2. 新用户登录获取 Token
|
||||
String userToken = loginAndGetToken("DEMO", uniqueUsername, "test1234");
|
||||
assertThat(userToken).isNotBlank();
|
||||
|
||||
// 3. 楠岃瘉锛欰NNOTATOR 鏃犳硶璁块棶寰呭鎵归槦鍒楋紙REVIEWER 涓撳睘锛夆啋 403
|
||||
// 3. 验证:ANNOTATOR 无法访问待审批队列(REVIEWER 专属)→ 403
|
||||
ResponseEntity<Map> beforeRoleChange = restTemplate.exchange(
|
||||
baseUrl("/api/tasks/pending-review"),
|
||||
HttpMethod.GET,
|
||||
@@ -71,7 +74,7 @@ public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
||||
Map.class);
|
||||
assertThat(beforeRoleChange.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
|
||||
// 4. ADMIN 鍙樻洿瑙掕壊涓?REVIEWER
|
||||
// 4. ADMIN 变更角色为 REVIEWER
|
||||
ResponseEntity<Map> roleResp = restTemplate.exchange(
|
||||
baseUrl("/api/users/" + newUserId + "/role"),
|
||||
HttpMethod.PUT,
|
||||
@@ -79,25 +82,25 @@ public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
||||
Map.class);
|
||||
assertThat(roleResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 5. 楠岃瘉锛氬悓涓€ Token 涓嬫璇锋眰绔嬪嵆鍏锋湁 REVIEWER 鏉冮檺 鈫?200
|
||||
// 5. 验证:同一 Token 下次请求立即具有 REVIEWER 权限 → 200
|
||||
ResponseEntity<Map> afterRoleChange = restTemplate.exchange(
|
||||
baseUrl("/api/tasks/pending-review"),
|
||||
HttpMethod.GET,
|
||||
bearerRequest(userToken),
|
||||
Map.class);
|
||||
assertThat(afterRoleChange.getStatusCode())
|
||||
.as("瑙掕壊鍙樻洿鍚庡悓涓€ Token 搴旂珛鍗冲叿鏈?REVIEWER 鏉冮檺")
|
||||
.as("角色变更后同一 Token 应立即具有 REVIEWER 权限")
|
||||
.isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 娴嬭瘯 2: 绂佺敤璐﹀彿 Token 绔嬪嵆澶辨晥 --
|
||||
// ------------------------------------------------------------------ 测试 2: 禁用账号 Token 立即失效 --
|
||||
|
||||
@Test
|
||||
@DisplayName("绂佺敤璐﹀彿鍚庯紝鐜版湁 Token 涓嬩竴娆¤姹傜珛鍗宠繑鍥?401")
|
||||
@DisplayName("禁用账号后,现有 Token 下一次请求立即返回 401")
|
||||
void disableAccount_tokenInvalidatedImmediately() {
|
||||
String uniqueUsername = "testuser-" + UUID.randomUUID().toString().substring(0, 8);
|
||||
|
||||
// 1. 鍒涘缓鐢ㄦ埛
|
||||
// 1. 创建用户
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + adminToken);
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
@@ -108,7 +111,7 @@ public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
||||
new HttpEntity<>(Map.of(
|
||||
"username", uniqueUsername,
|
||||
"password", "test1234",
|
||||
"realName", "娴嬭瘯鐢ㄦ埛",
|
||||
"realName", "测试用户",
|
||||
"role", "ANNOTATOR"
|
||||
), headers),
|
||||
Map.class);
|
||||
@@ -118,11 +121,11 @@ public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
||||
Map<String, Object> userData = (Map<String, Object>) createResp.getBody().get("data");
|
||||
Long newUserId = ((Number) userData.get("id")).longValue();
|
||||
|
||||
// 2. 鏂扮敤鎴风櫥褰曪紝鑾峰彇 Token
|
||||
// 2. 新用户登录,获取 Token
|
||||
String userToken = loginAndGetToken("DEMO", uniqueUsername, "test1234");
|
||||
assertThat(userToken).isNotBlank();
|
||||
|
||||
// 3. 楠岃瘉 Token 鏈夋晥
|
||||
// 3. 验证 Token 有效
|
||||
ResponseEntity<Map> meResp = restTemplate.exchange(
|
||||
baseUrl("/api/auth/me"),
|
||||
HttpMethod.GET,
|
||||
@@ -130,7 +133,7 @@ public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
||||
Map.class);
|
||||
assertThat(meResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 4. ADMIN 绂佺敤璐﹀彿
|
||||
// 4. ADMIN 禁用账号
|
||||
ResponseEntity<Map> disableResp = restTemplate.exchange(
|
||||
baseUrl("/api/users/" + newUserId + "/status"),
|
||||
HttpMethod.PUT,
|
||||
@@ -138,18 +141,18 @@ public class UserManagementIntegrationTest extends AbstractIntegrationTest {
|
||||
Map.class);
|
||||
assertThat(disableResp.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
// 5. 楠岃瘉锛氱鐢ㄥ悗锛岀幇鏈?Token 绔嬪嵆澶辨晥 鈫?401
|
||||
// 5. 验证:禁用后,现有 Token 立即失效 → 401
|
||||
ResponseEntity<Map> meAfterDisable = restTemplate.exchange(
|
||||
baseUrl("/api/auth/me"),
|
||||
HttpMethod.GET,
|
||||
bearerRequest(userToken),
|
||||
Map.class);
|
||||
assertThat(meAfterDisable.getStatusCode())
|
||||
.as("绂佺敤璐﹀彿鍚庣幇鏈?Token 搴旂珛鍗冲け鏁?)
|
||||
.as("禁用账号后现有 Token 应立即失效")
|
||||
.isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ 宸ュ叿鏂规硶 --
|
||||
// ------------------------------------------------------------------ 工具方法 --
|
||||
|
||||
private String loginAndGetToken(String companyCode, String username, String password) {
|
||||
LoginRequest req = new LoginRequest();
|
||||
|
||||
@@ -5,14 +5,14 @@ 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.dto.SourceResponse;
|
||||
import com.label.module.source.dto.SourceResponse;
|
||||
import com.label.module.task.controller.TaskController;
|
||||
import com.label.dto.TaskResponse;
|
||||
import com.label.module.task.dto.TaskResponse;
|
||||
import com.label.module.user.controller.AuthController;
|
||||
import com.label.module.user.controller.UserController;
|
||||
import com.label.dto.LoginRequest;
|
||||
import com.label.dto.LoginResponse;
|
||||
import com.label.dto.UserInfoResponse;
|
||||
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;
|
||||
@@ -32,7 +32,7 @@ import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("OpenAPI 娉ㄨВ瑕嗙洊娴嬭瘯")
|
||||
@DisplayName("OpenAPI 注解覆盖测试")
|
||||
class OpenApiAnnotationTest {
|
||||
|
||||
private static final List<Class<?>> CONTROLLERS = List.of(
|
||||
@@ -56,7 +56,7 @@ class OpenApiAnnotationTest {
|
||||
);
|
||||
|
||||
@Test
|
||||
@DisplayName("鎵€鏈?REST Controller 閮藉0鏄?@Tag")
|
||||
@DisplayName("所有 REST Controller 都声明 @Tag")
|
||||
void allControllersHaveTag() {
|
||||
assertThat(CONTROLLERS)
|
||||
.allSatisfy(controller ->
|
||||
@@ -66,7 +66,7 @@ class OpenApiAnnotationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("鎵€鏈?REST endpoint 鏂规硶閮藉0鏄?@Operation")
|
||||
@DisplayName("所有 REST endpoint 方法都声明 @Operation")
|
||||
void allEndpointMethodsHaveOperation() {
|
||||
for (Class<?> controller : CONTROLLERS) {
|
||||
Arrays.stream(controller.getDeclaredMethods())
|
||||
@@ -79,7 +79,7 @@ class OpenApiAnnotationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("鏍稿績 DTO 閮藉0鏄?@Schema")
|
||||
@DisplayName("核心 DTO 都声明 @Schema")
|
||||
void coreDtosHaveSchema() {
|
||||
assertThat(DTOS)
|
||||
.allSatisfy(dto ->
|
||||
|
||||
@@ -29,39 +29,6 @@ class PackageStructureMigrationTest {
|
||||
assertClassMissing("com.label.module.annotation.service.ExtractionApprovedEventListener");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("DTO、实体、Mapper 已迁移到扁平数据层")
|
||||
void dataTypesMoved() {
|
||||
for (String fqcn : java.util.List.of(
|
||||
"com.label.dto.LoginRequest",
|
||||
"com.label.dto.LoginResponse",
|
||||
"com.label.dto.UserInfoResponse",
|
||||
"com.label.dto.TaskResponse",
|
||||
"com.label.dto.SourceResponse",
|
||||
"com.label.entity.AnnotationResult",
|
||||
"com.label.entity.TrainingDataset",
|
||||
"com.label.entity.SysConfig",
|
||||
"com.label.entity.ExportBatch",
|
||||
"com.label.entity.SourceData",
|
||||
"com.label.entity.AnnotationTask",
|
||||
"com.label.entity.AnnotationTaskHistory",
|
||||
"com.label.entity.SysCompany",
|
||||
"com.label.entity.SysUser",
|
||||
"com.label.entity.VideoProcessJob",
|
||||
"com.label.mapper.AnnotationResultMapper",
|
||||
"com.label.mapper.TrainingDatasetMapper",
|
||||
"com.label.mapper.SysConfigMapper",
|
||||
"com.label.mapper.ExportBatchMapper",
|
||||
"com.label.mapper.SourceDataMapper",
|
||||
"com.label.mapper.AnnotationTaskMapper",
|
||||
"com.label.mapper.TaskHistoryMapper",
|
||||
"com.label.mapper.SysCompanyMapper",
|
||||
"com.label.mapper.SysUserMapper",
|
||||
"com.label.mapper.VideoProcessJobMapper")) {
|
||||
assertClassExists(fqcn);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertClassExists(String fqcn) {
|
||||
assertThatCode(() -> Class.forName(fqcn)).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user