chore: 【ops】fsm和order单元测试1.0

This commit is contained in:
lzh
2026-01-06 10:53:37 +08:00
parent a11c801b93
commit 1f889b65bf
2 changed files with 607 additions and 0 deletions

View File

@@ -0,0 +1,248 @@
package com.viewsh.module.ops.service.fsm;
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
import com.viewsh.module.ops.enums.OperatorTypeEnum;
import com.viewsh.module.ops.enums.PriorityEnum;
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
import com.viewsh.module.ops.service.event.OpsOrderEventService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
/**
* 工单状态机测试
*
* @author lzh
*/
@ExtendWith(MockitoExtension.class)
public class OrderStateMachineTest {
@InjectMocks
private OrderStateMachine orderStateMachine;
@Mock
private OpsOrderMapper opsOrderMapper;
@Mock
private OpsOrderEventService eventService;
private OpsOrderDO testOrder;
@BeforeEach
void setUp() {
// 初始化测试工单
testOrder = OpsOrderDO.builder()
.id(1L)
.orderCode("WO20250104143025001")
.orderType("CLEAN")
.status(WorkOrderStatusEnum.PENDING.getStatus())
.title("2楼电梯厅有水渍")
.priority(PriorityEnum.P2.getPriority())
.build();
}
@Test
void testTransition_PendingToAssigned_Success() {
// Given
WorkOrderStatusEnum fromStatus = WorkOrderStatusEnum.PENDING;
WorkOrderStatusEnum toStatus = WorkOrderStatusEnum.DISPATCHED;
// When
assertDoesNotThrow(() -> {
orderStateMachine.transition(
testOrder,
toStatus,
OperatorTypeEnum.SYSTEM,
null,
"系统自动分配"
);
});
// Then
assertEquals(WorkOrderStatusEnum.DISPATCHED.getStatus(), testOrder.getStatus());
verify(opsOrderMapper, times(1)).updateById((OpsOrderDO) any());
verify(eventService, times(1)).recordEvent(
anyLong(), anyString(), anyString(), anyString(), anyString(), any(), anyString()
);
}
@Test
void testTransition_AssignedToArrived_Success() {
// Given
testOrder.setStatus(WorkOrderStatusEnum.DISPATCHED.getStatus());
testOrder.setAssigneeId(1001L);
// When
assertDoesNotThrow(() -> {
orderStateMachine.transition(
testOrder,
WorkOrderStatusEnum.ARRIVED,
OperatorTypeEnum.CLEANER,
1001L,
"保洁员接单"
);
});
// Then
assertEquals(WorkOrderStatusEnum.ARRIVED.getStatus(), testOrder.getStatus());
assertNotNull(testOrder.getStartTime());
}
@Test
void testTransition_ArrivedToCompleted_Success() {
// Given
testOrder.setStatus(WorkOrderStatusEnum.ARRIVED.getStatus());
testOrder.setAssigneeId(1001L);
// When
assertDoesNotThrow(() -> {
orderStateMachine.transition(
testOrder,
WorkOrderStatusEnum.COMPLETED,
OperatorTypeEnum.CLEANER,
1001L,
"完成工单"
);
});
// Then
assertEquals(WorkOrderStatusEnum.COMPLETED.getStatus(), testOrder.getStatus());
assertNotNull(testOrder.getEndTime());
}
@Test
void testTransition_InvalidTransition_ThrowsException() {
// Given: 尝试非法转换 PENDING -> COMPLETED
WorkOrderStatusEnum toStatus = WorkOrderStatusEnum.COMPLETED;
// When & Then
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
orderStateMachine.transition(
testOrder,
toStatus,
OperatorTypeEnum.SYSTEM,
null,
"测试非法转换"
);
});
assertTrue(exception.getMessage().contains("非法状态转换"));
}
@Test
void testTransition_PendingToCancelled_Success() {
// Given
WorkOrderStatusEnum toStatus = WorkOrderStatusEnum.CANCELLED;
// When
assertDoesNotThrow(() -> {
orderStateMachine.transition(
testOrder,
toStatus,
OperatorTypeEnum.ADMIN,
1002L,
"取消工单"
);
});
// Then
assertEquals(WorkOrderStatusEnum.CANCELLED.getStatus(), testOrder.getStatus());
assertNotNull(testOrder.getEndTime());
}
@Test
void testTransition_ArrivedToPaused_Success() {
// Given
testOrder.setStatus(WorkOrderStatusEnum.ARRIVED.getStatus());
testOrder.setAssigneeId(1001L);
// When
assertDoesNotThrow(() -> {
orderStateMachine.transition(
testOrder,
WorkOrderStatusEnum.PAUSED,
OperatorTypeEnum.CLEANER,
1001L,
"P0紧急工单插队"
);
});
// Then
assertEquals(WorkOrderStatusEnum.PAUSED.getStatus(), testOrder.getStatus());
}
@Test
void testTransition_PausedToArrived_Success() {
// Given
testOrder.setStatus(WorkOrderStatusEnum.PAUSED.getStatus());
testOrder.setAssigneeId(1001L);
// When
assertDoesNotThrow(() -> {
orderStateMachine.transition(
testOrder,
WorkOrderStatusEnum.ARRIVED,
OperatorTypeEnum.CLEANER,
1001L,
"恢复工单"
);
});
// Then
assertEquals(WorkOrderStatusEnum.ARRIVED.getStatus(), testOrder.getStatus());
}
@Test
void testTransition_NullOrder_ThrowsException() {
// Given
OpsOrderDO nullOrder = null;
// When & Then
assertThrows(IllegalArgumentException.class, () -> {
orderStateMachine.transition(
nullOrder,
WorkOrderStatusEnum.DISPATCHED,
OperatorTypeEnum.SYSTEM,
null,
"测试"
);
});
}
@Test
void testGetAllowedTransitions_PendingStatus() {
// Given
WorkOrderStatusEnum currentStatus = WorkOrderStatusEnum.PENDING;
// When
var allowedTransitions = orderStateMachine.getAllowedTransitions(currentStatus);
// Then
assertNotNull(allowedTransitions);
assertEquals(2, allowedTransitions.size());
assertTrue(allowedTransitions.contains(WorkOrderStatusEnum.DISPATCHED));
assertTrue(allowedTransitions.contains(WorkOrderStatusEnum.CANCELLED));
}
@Test
void testGetAllowedTransitions_CompletedStatus() {
// Given
WorkOrderStatusEnum currentStatus = WorkOrderStatusEnum.COMPLETED;
// When
var allowedTransitions = orderStateMachine.getAllowedTransitions(currentStatus);
// Then
assertNotNull(allowedTransitions);
assertTrue(allowedTransitions.isEmpty()); // 终态,无后续转换
}
}

View File

@@ -0,0 +1,359 @@
package com.viewsh.module.ops.service.order;
import com.viewsh.framework.common.pojo.PageResult;
import com.viewsh.module.ops.dal.dataobject.dto.*;
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
import com.viewsh.module.ops.enums.OperatorTypeEnum;
import com.viewsh.module.ops.enums.PriorityEnum;
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
import com.viewsh.module.ops.service.fsm.OrderStateMachine;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
/**
* 工单服务测试
*
* @author lzh
*/
@ExtendWith(MockitoExtension.class)
public class OpsOrderServiceTest {
@Mock
private OpsOrderMapper opsOrderMapper;
@Mock
private OrderStateMachine orderStateMachine;
@InjectMocks
private OpsOrderServiceImpl opsOrderService;
private OpsOrderDO testOrder;
@BeforeEach
void setUp() {
// 初始化测试工单
testOrder = OpsOrderDO.builder()
.id(1L)
.orderCode("WO20250104143025001")
.orderType("CLEAN")
.status(WorkOrderStatusEnum.PENDING.getStatus())
.title("2楼电梯厅有水渍")
.priority(PriorityEnum.P2.getPriority())
.areaId(100L)
.location("2楼电梯厅")
.inspectorId(1001L)
.build();
}
@Test
void testCreateOrder_Success() {
// Given
OpsOrderCreateReqDTO createReq = new OpsOrderCreateReqDTO();
createReq.setOrderType("CLEAN");
createReq.setSourceType("MANUAL");
createReq.setTitle("2楼电梯厅有水渍");
createReq.setDescription("需要紧急处理");
createReq.setPriority(PriorityEnum.P0.getPriority());
createReq.setAreaId(100L);
createReq.setLocation("2楼电梯厅");
createReq.setInspectorId(1001L);
lenient().when(opsOrderMapper.insert((OpsOrderDO) any())).thenAnswer(invocation -> {
OpsOrderDO order = invocation.getArgument(0);
// 模拟 MyBatis Plus 的 ID 自动回填
if (order.getId() == null) {
order.setId(1L);
}
return 1;
});
lenient().doAnswer(invocation -> {
OpsOrderDO order = invocation.getArgument(0);
order.setStatus(WorkOrderStatusEnum.PENDING.getStatus());
return null;
}).when(orderStateMachine).transition(
any(OpsOrderDO.class),
any(WorkOrderStatusEnum.class),
any(OperatorTypeEnum.class),
anyLong(),
anyString()
);
// When
Long orderId = opsOrderService.createOrder(createReq);
// Then
assertNotNull(orderId);
verify(opsOrderMapper, times(1)).insert((OpsOrderDO) any());
verify(orderStateMachine, times(1)).transition(
any(OpsOrderDO.class),
eq(WorkOrderStatusEnum.PENDING),
eq(OperatorTypeEnum.SYSTEM),
eq(null),
eq("创建工单")
);
}
@Test
void testCreateOrder_WithDefaults() {
// Given: 不指定优先级和状态
OpsOrderCreateReqDTO createReq = new OpsOrderCreateReqDTO();
createReq.setOrderType("CLEAN");
createReq.setTitle("测试工单");
when(opsOrderMapper.insert((OpsOrderDO) any())).thenReturn(1);
// When
opsOrderService.createOrder(createReq);
// Then: 验证默认值设置
verify(opsOrderMapper).insert(argThat((OpsOrderDO order) ->
order.getPriority().equals(PriorityEnum.P2.getPriority()) &&
order.getStatus().equals(WorkOrderStatusEnum.PENDING.getStatus()) &&
order.getSourceType().equals("MANUAL")
));
}
@Test
void testUpdateOrder_Success() {
// Given
OpsOrderUpdateReqDTO updateReq = new OpsOrderUpdateReqDTO();
updateReq.setId(1L);
updateReq.setTitle("更新后的标题");
updateReq.setDescription("更新后的描述");
updateReq.setPriority(PriorityEnum.P0.getPriority());
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
when(opsOrderMapper.updateById((OpsOrderDO) any())).thenReturn(1);
// When
assertDoesNotThrow(() -> opsOrderService.updateOrder(updateReq));
// Then
verify(opsOrderMapper, times(1)).updateById((OpsOrderDO) any());
}
@Test
void testUpdateOrder_OrderNotFound_ThrowsException() {
// Given
OpsOrderUpdateReqDTO updateReq = new OpsOrderUpdateReqDTO();
updateReq.setId(999L);
when(opsOrderMapper.selectById(999L)).thenReturn(null);
// When & Then
assertThrows(RuntimeException.class, () -> opsOrderService.updateOrder(updateReq));
}
@Test
void testDeleteOrder_Success() {
// Given
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
when(opsOrderMapper.deleteById(1L)).thenReturn(1);
// When
assertDoesNotThrow(() -> opsOrderService.deleteOrder(1L));
// Then
verify(opsOrderMapper, times(1)).deleteById(1L);
}
@Test
void testDeleteOrder_NotPendingStatus_ThrowsException() {
// Given: 工单状态为ARRIVED不能删除
testOrder.setStatus(WorkOrderStatusEnum.ARRIVED.getStatus());
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
// When & Then
assertThrows(RuntimeException.class, () -> opsOrderService.deleteOrder(1L));
}
@Test
void testGetOrder_Success() {
// Given
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
// When
OpsOrderDO order = opsOrderService.getOrder(1L);
// Then
assertNotNull(order);
assertEquals(1L, order.getId());
assertEquals("WO20250104143025001", order.getOrderCode());
}
@Test
void testGetOrder_NotFound_ThrowsException() {
// Given
when(opsOrderMapper.selectById(999L)).thenReturn(null);
// When & Then
assertThrows(RuntimeException.class, () -> opsOrderService.getOrder(999L));
}
@Test
void testAssignOrder_Success() {
// Given
OpsOrderAssignReqDTO assignReq = new OpsOrderAssignReqDTO();
assignReq.setOrderId(1L);
assignReq.setAssigneeId(2001L);
assignReq.setRemark("张师傅负责该区域");
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
// When
assertDoesNotThrow(() -> opsOrderService.assignOrder(assignReq, OperatorTypeEnum.ADMIN, 1002L));
// Then
verify(orderStateMachine, times(1)).transition(
eq(testOrder),
eq(WorkOrderStatusEnum.PENDING),
eq(OperatorTypeEnum.ADMIN),
eq(1002L),
eq("张师傅负责该区域")
);
assertEquals(2001L, testOrder.getAssigneeId());
}
@Test
void testAcceptOrder_Success() {
// Given
testOrder.setStatus(WorkOrderStatusEnum.PENDING.getStatus());
testOrder.setAssigneeId(2001L);
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
// When
assertDoesNotThrow(() -> opsOrderService.acceptOrder(1L, 2001L));
// Then
verify(orderStateMachine, times(1)).transition(
eq(testOrder),
eq(WorkOrderStatusEnum.ARRIVED),
eq(OperatorTypeEnum.CLEANER),
eq(2001L),
eq("保洁员接单")
);
}
@Test
void testAcceptOrder_WrongAssignee_ThrowsException() {
// Given
testOrder.setStatus(WorkOrderStatusEnum.PENDING.getStatus());
testOrder.setAssigneeId(2001L); // 分配给张师傅
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
// When & Then: 李师傅尝试接单
assertThrows(RuntimeException.class, () -> opsOrderService.acceptOrder(1L, 2002L));
}
@Test
void testCompleteOrder_Success() {
// Given
OpsOrderCompleteReqDTO completeReq = new OpsOrderCompleteReqDTO();
completeReq.setOrderId(1L);
completeReq.setRemark("已完成清洁");
testOrder.setStatus(WorkOrderStatusEnum.ARRIVED.getStatus());
testOrder.setAssigneeId(2001L);
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
// When
assertDoesNotThrow(() -> opsOrderService.completeOrder(completeReq, 2001L));
// Then
verify(orderStateMachine, times(1)).transition(
eq(testOrder),
eq(WorkOrderStatusEnum.COMPLETED),
eq(OperatorTypeEnum.CLEANER),
eq(2001L),
eq("已完成清洁")
);
}
@Test
void testPauseOrder_Success() {
// Given
testOrder.setStatus(WorkOrderStatusEnum.ARRIVED.getStatus());
testOrder.setAssigneeId(2001L);
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
// When
assertDoesNotThrow(() -> opsOrderService.pauseOrder(1L, 2001L, "P0紧急工单插队"));
// Then
verify(orderStateMachine, times(1)).transition(
eq(testOrder),
eq(WorkOrderStatusEnum.PAUSED),
eq(OperatorTypeEnum.CLEANER),
eq(2001L),
eq("P0紧急工单插队")
);
}
@Test
void testResumeOrder_Success() {
// Given
testOrder.setStatus(WorkOrderStatusEnum.PAUSED.getStatus());
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
// When
assertDoesNotThrow(() -> opsOrderService.resumeOrder(1L, 2001L));
// Then
verify(orderStateMachine, times(1)).transition(
eq(testOrder),
eq(WorkOrderStatusEnum.ARRIVED),
eq(OperatorTypeEnum.CLEANER),
eq(2001L),
eq("恢复工单")
);
}
@Test
void testCancelOrder_Success() {
// Given
testOrder.setStatus(WorkOrderStatusEnum.PENDING.getStatus());
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
// When
assertDoesNotThrow(() -> opsOrderService.cancelOrder(1L, "测试取消", OperatorTypeEnum.ADMIN, 1002L));
// Then
verify(orderStateMachine, times(1)).transition(
eq(testOrder),
eq(WorkOrderStatusEnum.CANCELLED),
eq(OperatorTypeEnum.ADMIN),
eq(1002L),
eq("测试取消")
);
}
@Test
void testCancelOrder_CompletedOrder_ThrowsException() {
// Given
testOrder.setStatus(WorkOrderStatusEnum.COMPLETED.getStatus());
when(opsOrderMapper.selectById(1L)).thenReturn(testOrder);
// When & Then
assertThrows(RuntimeException.class,
() -> opsOrderService.cancelOrder(1L, "测试取消", OperatorTypeEnum.ADMIN, 1002L));
}
}