✅ 补个人认证「提交→审核→生效」闭环测试
司机认证最关键的一条业务闭环,单看提交侧或审核侧都验证不到: 提交之后司机侧查到的必须仍是旧生效值(否则等于提交即自动认证), 审核通过之后才变成新值(否则认证永远不生效)。 用内存 Map 承接记录读写,让提交服务与审核服务真实共享同一份状态, 避免两边各自 mock 成「恰好一致」——那种测试对集成缺陷是盲的。 5 个用例覆盖: - 提交后仍是旧值、审核通过后才变新值; - 驳回后生效值不变(已认证司机不会被驳回下线); - 从未认证时审核前查不到生效值; - 连续提交两次只剩一条待审,且被作废的那条不能再被审核; - 已通过的记录不可再审(防止把生效快照改坏)。 全模块 63 个测试通过:mvn -o test -pl blade-service/blade-transport -am
This commit is contained in:
+217
@@ -0,0 +1,217 @@
|
|||||||
|
package org.springblade.transport.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springblade.transport.pojo.dto.PersonCertSubmitRequest;
|
||||||
|
import org.springblade.transport.pojo.entity.Driver;
|
||||||
|
import org.springblade.transport.pojo.entity.TransportCertRecord;
|
||||||
|
import org.springblade.transport.service.IDriverAppService;
|
||||||
|
import org.springblade.transport.service.IDriverService;
|
||||||
|
import org.springblade.transport.service.ITransportCertRecordService;
|
||||||
|
import org.springblade.transport.service.ITransportVehicleService;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 个人认证的「提交 → 生效值不变 → 审核通过 → 生效值更新」闭环测试。
|
||||||
|
* <p>
|
||||||
|
* 这是司机认证最关键的一条业务闭环,也是单看提交侧或审核侧都验证不到的:
|
||||||
|
* 提交之后司机侧查到的必须仍是**旧生效值**(否则等于提交即自动认证),
|
||||||
|
* 审核通过之后才变成**新值**(否则认证永远不生效)。
|
||||||
|
* <p>
|
||||||
|
* 用一个内存版的记录存储把两侧接起来,避免两边各自 mock 成「恰好一致」。
|
||||||
|
*/
|
||||||
|
class PersonCertRoundTripTest {
|
||||||
|
|
||||||
|
private static final Long DRIVER_ID = 9001L;
|
||||||
|
|
||||||
|
private IDriverAppService driverAppService;
|
||||||
|
private IDriverService driverService;
|
||||||
|
private ITransportCertRecordService certRecordService;
|
||||||
|
private DriverCertServiceImpl driverCertService;
|
||||||
|
private CertAuditServiceImpl certAuditService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内存记录表:Id → 记录
|
||||||
|
*/
|
||||||
|
private final Map<Long, TransportCertRecord> recordStore = new HashMap<>();
|
||||||
|
private Driver driver;
|
||||||
|
private long idSequence = 100L;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
recordStore.clear();
|
||||||
|
idSequence = 100L;
|
||||||
|
|
||||||
|
driverAppService = mock(IDriverAppService.class);
|
||||||
|
driverService = mock(IDriverService.class);
|
||||||
|
certRecordService = mock(ITransportCertRecordService.class);
|
||||||
|
ITransportVehicleService transportVehicleService = mock(ITransportVehicleService.class);
|
||||||
|
|
||||||
|
driver = new Driver();
|
||||||
|
driver.setId(DRIVER_ID);
|
||||||
|
driver.setDriverName("张三");
|
||||||
|
when(driverAppService.currentDriverEntity()).thenAnswer(invocation -> driver);
|
||||||
|
// 司机表只持有指针,指针是被审核链路改的,这里让桩跟着内存状态走
|
||||||
|
when(driverService.updateById(any(Driver.class))).thenAnswer(invocation -> {
|
||||||
|
Driver patch = invocation.getArgument(0);
|
||||||
|
if (patch.getEffectiveCertRecordId() != null) {
|
||||||
|
driver.setEffectiveCertRecordId(patch.getEffectiveCertRecordId());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
stubRecordStore();
|
||||||
|
|
||||||
|
driverCertService = new DriverCertServiceImpl(driverAppService, driverService,
|
||||||
|
transportVehicleService, certRecordService);
|
||||||
|
certAuditService = new CertAuditServiceImpl(certRecordService, transportVehicleService, driverService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("提交后仍是旧生效值,审核通过后才变新值")
|
||||||
|
void submitThenApprove() {
|
||||||
|
// 1) 先走一遍完整流程,让该司机有一个生效值
|
||||||
|
Long firstRecordId = driverCertService.submitPersonCert(personCert("张三", "110101199001011234"));
|
||||||
|
certAuditService.audit(firstRecordId, true, null);
|
||||||
|
assertEquals("张三", driverCertService.getEffectivePersonCert().getRealName(),
|
||||||
|
"审核通过后应能查到生效值");
|
||||||
|
|
||||||
|
// 2) 提交新内容:生效值必须**不变**
|
||||||
|
Long secondRecordId = driverCertService.submitPersonCert(personCert("张三改名", "110101199001011234"));
|
||||||
|
assertEquals("张三", driverCertService.getEffectivePersonCert().getRealName(),
|
||||||
|
"提交只产生待审记录,不得改变生效值——否则等于提交即自动认证");
|
||||||
|
assertEquals(firstRecordId, driver.getEffectiveCertRecordId(),
|
||||||
|
"指针应仍指向原来那条已通过记录");
|
||||||
|
|
||||||
|
// 3) 审核通过:生效值才变成新值
|
||||||
|
certAuditService.audit(secondRecordId, true, null);
|
||||||
|
assertEquals("张三改名", driverCertService.getEffectivePersonCert().getRealName(),
|
||||||
|
"审核通过后生效值应切到新记录");
|
||||||
|
assertEquals(secondRecordId, driver.getEffectiveCertRecordId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("驳回后生效值不变(已认证对象不会被驳回下线)")
|
||||||
|
void rejectKeepsEffectiveValue() {
|
||||||
|
Long firstRecordId = driverCertService.submitPersonCert(personCert("张三", "110101199001011234"));
|
||||||
|
certAuditService.audit(firstRecordId, true, null);
|
||||||
|
|
||||||
|
Long secondRecordId = driverCertService.submitPersonCert(personCert("张三改名", "110101199001011234"));
|
||||||
|
certAuditService.audit(secondRecordId, false, "证件照片模糊");
|
||||||
|
|
||||||
|
assertEquals("张三", driverCertService.getEffectivePersonCert().getRealName(),
|
||||||
|
"驳回只终结该次提交,既得认证必须保持有效");
|
||||||
|
assertEquals(firstRecordId, driver.getEffectiveCertRecordId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("从未认证过时,审核通过前查不到生效值")
|
||||||
|
void noEffectiveBeforeFirstApproval() {
|
||||||
|
assertNull(driverCertService.getEffectivePersonCert());
|
||||||
|
|
||||||
|
Long recordId = driverCertService.submitPersonCert(personCert("张三", "110101199001011234"));
|
||||||
|
assertNull(driverCertService.getEffectivePersonCert(),
|
||||||
|
"待审期间不应有生效值");
|
||||||
|
|
||||||
|
certAuditService.audit(recordId, true, null);
|
||||||
|
assertEquals("张三", driverCertService.getEffectivePersonCert().getRealName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("连续提交两次,不存在两条待审,且旧待审被作废")
|
||||||
|
void consecutiveSubmitsLeaveSinglePending() {
|
||||||
|
Long first = driverCertService.submitPersonCert(personCert("第一次", "110101199001011234"));
|
||||||
|
Long second = driverCertService.submitPersonCert(personCert("第二次", "110101199001011234"));
|
||||||
|
|
||||||
|
assertEquals(TransportCertRecord.STATUS_SUPERSEDED, recordStore.get(first).getStatus(),
|
||||||
|
"前一条待审应被作废");
|
||||||
|
assertEquals(TransportCertRecord.STATUS_PENDING, recordStore.get(second).getStatus());
|
||||||
|
|
||||||
|
long pendingCount = recordStore.values().stream()
|
||||||
|
.filter(record -> TransportCertRecord.STATUS_PENDING.equals(record.getStatus()))
|
||||||
|
.count();
|
||||||
|
assertEquals(1L, pendingCount, "同一司机至多一条待审");
|
||||||
|
|
||||||
|
// 被作废的那条不能再被审核(否则会把已作废的记录变成生效值)
|
||||||
|
org.junit.jupiter.api.Assertions.assertThrows(
|
||||||
|
org.springblade.core.log.exception.ServiceException.class,
|
||||||
|
() -> certAuditService.audit(first, true, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("已通过的记录不可再被审核(防止把生效快照改坏)")
|
||||||
|
void approvedRecordCannotBeReaudited() {
|
||||||
|
Long recordId = driverCertService.submitPersonCert(personCert("张三", "110101199001011234"));
|
||||||
|
certAuditService.audit(recordId, true, null);
|
||||||
|
|
||||||
|
org.junit.jupiter.api.Assertions.assertThrows(
|
||||||
|
org.springblade.core.log.exception.ServiceException.class,
|
||||||
|
() -> certAuditService.audit(recordId, false, "事后反悔"));
|
||||||
|
assertEquals("张三", driverCertService.getEffectivePersonCert().getRealName(),
|
||||||
|
"重复审核被拒后,生效值不受影响");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------ 内存记录存储
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用内存 Map 承接记录读写,让两个服务真实地共享同一份状态
|
||||||
|
*/
|
||||||
|
private void stubRecordStore() {
|
||||||
|
when(certRecordService.save(any(TransportCertRecord.class))).thenAnswer(invocation -> {
|
||||||
|
TransportCertRecord record = invocation.getArgument(0);
|
||||||
|
record.setId(++idSequence);
|
||||||
|
recordStore.put(record.getId(), copyOf(record));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
when(certRecordService.updateById(any(TransportCertRecord.class))).thenAnswer(invocation -> {
|
||||||
|
TransportCertRecord patch = invocation.getArgument(0);
|
||||||
|
TransportCertRecord stored = recordStore.get(patch.getId());
|
||||||
|
if (stored != null) {
|
||||||
|
if (patch.getStatus() != null) {
|
||||||
|
stored.setStatus(patch.getStatus());
|
||||||
|
}
|
||||||
|
stored.setRejectReason(patch.getRejectReason());
|
||||||
|
stored.setAuditTime(patch.getAuditTime());
|
||||||
|
stored.setAuditUser(patch.getAuditUser());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
when(certRecordService.getById(anyLong()))
|
||||||
|
.thenAnswer(invocation -> recordStore.get(invocation.getArgument(0, Long.class)));
|
||||||
|
when(certRecordService.selectPendingRecord(any(), anyLong())).thenAnswer(invocation -> {
|
||||||
|
String bizType = invocation.getArgument(0);
|
||||||
|
Long bizId = invocation.getArgument(1);
|
||||||
|
return recordStore.values().stream()
|
||||||
|
.filter(record -> bizType.equals(record.getBizType())
|
||||||
|
&& bizId.equals(record.getBizId())
|
||||||
|
&& TransportCertRecord.STATUS_PENDING.equals(record.getStatus()))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private TransportCertRecord copyOf(TransportCertRecord source) {
|
||||||
|
return JSON.parseObject(JSON.toJSONString(source), TransportCertRecord.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PersonCertSubmitRequest personCert(String realName, String idCardNo) {
|
||||||
|
PersonCertSubmitRequest request = new PersonCertSubmitRequest();
|
||||||
|
request.setRealName(realName);
|
||||||
|
request.setIdCardNo(idCardNo);
|
||||||
|
request.setIdCardFrontUrl("http://example.com/front.jpg");
|
||||||
|
request.setIdCardBackUrl("http://example.com/back.jpg");
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user