fix: reject unavailable self-operated collection commands before initialization
This commit is contained in:
+7
-6
@@ -26,7 +26,7 @@ class SelfopCommandServiceTest {
|
||||
configure();
|
||||
var original = original("synthetic-account", "WH-B");
|
||||
when(tasks.selectOne(any())).thenAnswer(call -> { com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<SelfopSyncTask> q=call.getArgument(0); q.getSqlSegment(); return q.getParamNameValuePairs().containsValue(original.getSourceAccount())?original:null; });
|
||||
assertThatThrownBy(() -> service.request(retry(), "42")).hasMessage("SELFOP_TASK_SCOPE_MISMATCH");
|
||||
assertThatThrownBy(() -> service.request(retry(), "42")).hasMessage("任务不存在或不属于当前账号和仓库");
|
||||
verify(taskService, never()).retry(anyLong(), anyString(), anyString(), anyString());
|
||||
verify(taskService, never()).create(any(SelfopSyncTask.class));
|
||||
assertThat(original.getStatus()).isEqualTo("DEAD");
|
||||
@@ -36,7 +36,7 @@ class SelfopCommandServiceTest {
|
||||
configure();
|
||||
var original = original("different-source-account", "WH-A");
|
||||
when(tasks.selectOne(any())).thenAnswer(call -> { com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<SelfopSyncTask> q=call.getArgument(0); q.getSqlSegment(); return q.getParamNameValuePairs().containsValue(original.getSourceAccount())?original:null; });
|
||||
assertThatThrownBy(() -> service.request(retry(), "42")).hasMessage("SELFOP_TASK_SCOPE_MISMATCH");
|
||||
assertThatThrownBy(() -> service.request(retry(), "42")).hasMessage("任务不存在或不属于当前账号和仓库");
|
||||
verify(taskService, never()).retry(anyLong(), anyString(), anyString(), anyString());
|
||||
verify(taskService, never()).create(any(SelfopSyncTask.class));
|
||||
assertThat(original.getStatus()).isEqualTo("DEAD");
|
||||
@@ -77,16 +77,17 @@ class SelfopCommandServiceTest {
|
||||
configure();config.setCollectionMode("OFF");
|
||||
assertThat(config.isExecutionEnabled()).isFalse();
|
||||
var c=new SelfopCommandService.Command("disabled","WH-A","INVENTORY",null,null,null,null,null,"offline test");
|
||||
assertThatThrownBy(()->service.request(c,"42")).hasMessage("SELFOP_PURPOSE_INVALID");
|
||||
assertThatThrownBy(()->service.request(c,"42")).hasMessage("采集功能未启用,当前环境不允许执行采集或重试");
|
||||
assertThatThrownBy(()->service.request(retry(),"42")).hasMessage("采集功能未启用,当前环境不允许执行采集或重试");
|
||||
config.setCollectionMode("BUSINESS");config.setEnvironment("test");
|
||||
assertThat(config.isExecutionEnabled()).isFalse();
|
||||
assertThatThrownBy(()->service.request(c,"42")).hasMessage("SELFOP_BUSINESS_ENVIRONMENT_REJECTED");
|
||||
verify(taskService,never()).create(any(SelfopSyncTask.class));
|
||||
assertThatThrownBy(()->service.request(c,"42")).hasMessage("采集功能未启用,当前环境不允许执行采集或重试");
|
||||
verifyNoInteractions(taskService,scopes,accounts,tasks);
|
||||
}
|
||||
@Test void validationModeRejectsFormalInventoryEntry() {
|
||||
configure();config.setCollectionMode("VALIDATION");
|
||||
var c=new SelfopCommandService.Command("wrong-purpose","WH-A","INVENTORY",null,null,null,null,null,"offline test");
|
||||
assertThatThrownBy(()->service.request(c,"42")).hasMessage("SELFOP_VALIDATION_ENTRY_REQUIRED");
|
||||
assertThatThrownBy(()->service.request(c,"42")).hasMessage("当前采集模式与所选操作不一致,请使用对应入口");
|
||||
verify(taskService,never()).create(any(SelfopSyncTask.class));
|
||||
}
|
||||
private SelfopCommandService.Command retry() {
|
||||
|
||||
+84
@@ -13,6 +13,17 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import java.util.Set;
|
||||
import com.baiyue.common.web.GlobalExceptionHandler;
|
||||
import com.baiyue.lastleg.selfoperated.analytics.model.SelfopSupport;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@@ -88,6 +99,79 @@ class SyncControllerTest {
|
||||
assertThat(method.getAnnotation(PreAuthorize.class).value()).isEqualTo("hasAuthority('" + SYNC + "')");
|
||||
}
|
||||
|
||||
@Test void offAndRetryAreChineseBusinessRejectionsBeforeAnyInitialization() throws Exception {
|
||||
var config=new SelfopAnalyticsProperties();
|
||||
var tasks=mock(SelfopTaskService.class);var scopes=mock(SelfopSyncScopeMapper.class);
|
||||
var accounts=mock(SelfopOutboundDiscoveryScopeMapper.class);var rows=mock(SelfopSyncTaskMapper.class);
|
||||
var mvc=mvc(new SelfopCommandService(tasks,scopes,accounts,config,rows));
|
||||
rejected(mvc,command,"采集功能未启用,当前环境不允许执行采集或重试");
|
||||
rejected(mvc,new SelfopCommandService.Command("retry","WH-A","RETRY","LXCOL-1",null,null,null,null,"原因"),
|
||||
"采集功能未启用,当前环境不允许执行采集或重试");
|
||||
verifyNoInteractions(tasks,scopes,accounts,rows);
|
||||
}
|
||||
|
||||
@Test void unconfiguredWarehouseAndInvalidPublicInputsRejectBeforeInitialization() throws Exception {
|
||||
var config=new SelfopAnalyticsProperties();config.setCollectionMode("BUSINESS");config.setEnvironment("production");
|
||||
var tasks=mock(SelfopTaskService.class);var scopes=mock(SelfopSyncScopeMapper.class);
|
||||
var accounts=mock(SelfopOutboundDiscoveryScopeMapper.class);var rows=mock(SelfopSyncTaskMapper.class);
|
||||
var mvc=mvc(new SelfopCommandService(tasks,scopes,accounts,config,rows));
|
||||
rejected(mvc,command,"该仓库尚未配置采集,暂不能提交任务");
|
||||
config.getWarehouses().put("WH-A",new SelfopAnalyticsProperties.Warehouse());
|
||||
rejected(mvc,new SelfopCommandService.Command("invalid","WH-A",null,null,null,null,null,null,null),"不支持此采集操作");
|
||||
rejected(mvc,new SelfopCommandService.Command("invalid","WH-A","BACKFILL",null,null,null,null,null,null),
|
||||
"采集时间范围不能为空,结束时间必须晚于开始时间");
|
||||
rejected(mvc,new SelfopCommandService.Command("invalid","WH-A","RETRY","LXCOL-1",null,null,null,null," "),
|
||||
"重试原因不能为空且不能超过500个字符");
|
||||
verifyNoInteractions(tasks,scopes,accounts,rows);
|
||||
}
|
||||
|
||||
@Test void controlledTaskConflictIsBusinessRejectionButUnknownFailureRemainsSystemError() throws Exception {
|
||||
var config=new SelfopAnalyticsProperties();config.setCollectionMode("BUSINESS");config.setEnvironment("production");
|
||||
config.setSourceAccount("synthetic-account");config.getWarehouses().put("WH-A",new SelfopAnalyticsProperties.Warehouse());
|
||||
var tasks=mock(SelfopTaskService.class);var scopes=mock(SelfopSyncScopeMapper.class);
|
||||
var accounts=mock(SelfopOutboundDiscoveryScopeMapper.class);var rows=mock(SelfopSyncTaskMapper.class);
|
||||
var scope=new SelfopSyncScope();scope.setId(10L);scope.setScopeHash("hash");when(scopes.selectOne(any())).thenReturn(scope);
|
||||
var mvc=mvc(new SelfopCommandService(tasks,scopes,accounts,config,rows));
|
||||
doThrow(new IllegalStateException("SELFOP_IDEMPOTENCY_CONFLICT")).when(tasks).create(any());
|
||||
rejected(mvc,command,"请求标识已用于其他内容,请刷新后重新提交");
|
||||
doThrow(new IllegalStateException("unknown runtime failure")).when(tasks).create(any());
|
||||
mvc.perform(post("/api/platform/v1/last-leg/self-operated-analytics/sync-tasks")
|
||||
.contentType("application/json").content(SelfopSupport.json(command)))
|
||||
// The platform handler transports all result codes in HTTP 200; unknown failures retain S0500.
|
||||
.andExpect(status().isOk()).andExpect(jsonPath("$.code").value("S0500"));
|
||||
}
|
||||
|
||||
@Test void runningRetryIsControlledAndDoesNotInitializeScopes() throws Exception {
|
||||
var config=new SelfopAnalyticsProperties();config.setCollectionMode("BUSINESS");config.setEnvironment("production");
|
||||
config.setSourceAccount("synthetic-account");config.getWarehouses().put("WH-A",new SelfopAnalyticsProperties.Warehouse());
|
||||
var tasks=mock(SelfopTaskService.class);var scopes=mock(SelfopSyncScopeMapper.class);
|
||||
var accounts=mock(SelfopOutboundDiscoveryScopeMapper.class);var rows=mock(SelfopSyncTaskMapper.class);
|
||||
var original=new SelfopSyncTask();original.setId(1L);original.setTaskRole("WAREHOUSE_SYNC");
|
||||
original.setWarehouseCode("WH-A");original.setExecutionPurpose("BUSINESS");original.setStatus("FETCHING");
|
||||
when(rows.selectOne(any())).thenReturn(original);
|
||||
doThrow(new IllegalStateException("SELFOP_RETRY_LEASE_BUSY")).when(tasks).retry(1L,"retry","42","原因");
|
||||
rejected(mvc(new SelfopCommandService(tasks,scopes,accounts,config,rows)),
|
||||
new SelfopCommandService.Command("retry","WH-A","RETRY","LXCOL-1",null,null,null,null,"原因"),
|
||||
"任务正在执行,暂不能重试");
|
||||
verify(tasks,never()).initialize();verify(tasks,never()).create(any());verifyNoInteractions(scopes,accounts);
|
||||
}
|
||||
|
||||
private void rejected(MockMvc mvc,SelfopCommandService.Command request,String message) throws Exception {
|
||||
mvc.perform(post("/api/platform/v1/last-leg/self-operated-analytics/sync-tasks")
|
||||
.contentType("application/json").content(SelfopSupport.json(request)))
|
||||
.andExpect(status().isOk()).andExpect(jsonPath("$.code").value("B0001"))
|
||||
.andExpect(jsonPath("$.message").value(message));
|
||||
}
|
||||
private MockMvc mvc(SelfopCommandService commands){
|
||||
return MockMvcBuilders.standaloneSetup(new SyncController(commands)).setControllerAdvice(new GlobalExceptionHandler())
|
||||
.setCustomArgumentResolvers(new HandlerMethodArgumentResolver(){
|
||||
public boolean supportsParameter(MethodParameter parameter){return parameter.getParameterType()==AuthenticatedUser.class;}
|
||||
public Object resolveArgument(MethodParameter parameter,ModelAndViewContainer container,NativeWebRequest request,WebDataBinderFactory factory){
|
||||
return user(0L,Set.of(SYNC));
|
||||
}
|
||||
}).build();
|
||||
}
|
||||
|
||||
private AuthenticatedUser user(Long customerScope, Set<String> permissions) {
|
||||
return new AuthenticatedUser(42L, customerScope, 0L, 1L, "synthetic-user", "PLATFORM", "WEB", permissions);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user