feat(p3.5): oapi 增 GET /tasks/done 与 /cc——已办/抄送按签名方应用过滤(P2-02 接缝)
build / build-push (push) Successful in 1m11s
build / build-push (push) Successful in 1m11s
- 两端点复用 FlowQueryService#pageDone/pageCc;过滤键=验签身份 appKey(请求属性 yh.app.auth → d.appKey),不收调用方 bizType——天然覆盖消费方新旧 bizType 存量、 防共享定义跨应用泄漏 - 契约测试 5 例(standalone 无 DB):appKey 接缝下传/bizType 不参与过滤/必参缺失 400 零查询 - 全量 262/262 绿(本地含 DB;Stage S5/P3.5)
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
package com.yunhai.flowcenter.oapi;
|
||||
|
||||
import com.yunhai.flowcenter.controller.oapi.OapiFlowController;
|
||||
import com.yunhai.flowcenter.model.FlowModels;
|
||||
import com.yunhai.flowcenter.service.FlowQueryService;
|
||||
import com.yunhai.flowcenter.service.FlowRouterAdminService;
|
||||
import com.yunhai.flowcenter.service.OapiFlowService;
|
||||
import com.yunhai.platform.utils.model.BasePageResult;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* P3.5 新增 oapi 端点(/oapi/flow/tasks/done、/oapi/flow/cc)的控制器契约测试(standalone,无 DB)。
|
||||
* 核心断言=<b>过滤接缝</b>(P2-02 裁决):过滤键是验签身份 appKey(请求属性 yh.app.auth →
|
||||
* queryService 第三参,落 d.appKey 过滤),<b>不收调用方 bizType</b>——bizType 参数即使传了
|
||||
* 也不参与过滤(防裸透传/跨应用探查)。数据正确性由既有 pageDone/pageCc 的 IT 覆盖,
|
||||
* 本类只钉 HTTP 契约与接缝。
|
||||
*/
|
||||
@DisplayName("P3.5 oapi done/cc - 签名 appKey 过滤接缝契约")
|
||||
class OapiFlowP35DoneCcContractTest {
|
||||
|
||||
private FlowQueryService queryService;
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
OapiFlowService flowService = mock(OapiFlowService.class);
|
||||
FlowRouterAdminService routerAdminService = mock(FlowRouterAdminService.class);
|
||||
queryService = mock(FlowQueryService.class);
|
||||
OapiFlowController controller = new OapiFlowController(flowService, routerAdminService, queryService);
|
||||
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
when(queryService.pageDone(any(), any(), any(), any(), any()))
|
||||
.thenReturn(emptyPage());
|
||||
when(queryService.pageCc(any(), any(), any(), any(), any()))
|
||||
.thenReturn(new BasePageResult<>(Collections.emptyList(), 0, 1, 10, 0, false));
|
||||
}
|
||||
|
||||
private static BasePageResult<FlowModels.TaskItem> emptyPage() {
|
||||
return new BasePageResult<>(Collections.emptyList(), 0, 1, 10, 0, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("done:验签 appKey 作为过滤键下传(d.appKey 接缝),分页参数透传")
|
||||
void donePassesSigningAppKeyAsFilter() throws Exception {
|
||||
mockMvc.perform(get("/oapi/flow/tasks/done")
|
||||
.param("schId", "3").param("assigneeId", "10006")
|
||||
.param("page", "2").param("size", "10")
|
||||
.requestAttr("yh.app.auth", "100008"))
|
||||
.andExpect(status().isOk());
|
||||
verify(queryService).pageDone(eq(3L), eq(10006), eq("100008"), eq(2), eq(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("done:调用方带 bizType 也不参与过滤(防裸透传)")
|
||||
void doneIgnoresCallerBizType() throws Exception {
|
||||
mockMvc.perform(get("/oapi/flow/tasks/done")
|
||||
.param("schId", "3").param("assigneeId", "10006")
|
||||
.param("bizType", "svc-hall")
|
||||
.requestAttr("yh.app.auth", "100008"))
|
||||
.andExpect(status().isOk());
|
||||
// 第三参(过滤键)必须是签名 appKey,不是 bizType
|
||||
verify(queryService).pageDone(eq(3L), eq(10006), eq("100008"), Mockito.isNull(), Mockito.isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("done:缺 schId/assigneeId → 400,零查询")
|
||||
void doneMissingRequiredParams() throws Exception {
|
||||
mockMvc.perform(get("/oapi/flow/tasks/done").param("schId", "3"))
|
||||
.andExpect(status().is4xxClientError());
|
||||
verify(queryService, never()).pageDone(any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("cc:验签 appKey 过滤键 + userId 维度")
|
||||
void ccPassesSigningAppKeyAsFilter() throws Exception {
|
||||
mockMvc.perform(get("/oapi/flow/cc")
|
||||
.param("schId", "3").param("userId", "10006")
|
||||
.param("page", "1").param("size", "20")
|
||||
.requestAttr("yh.app.auth", "100008"))
|
||||
.andExpect(status().isOk());
|
||||
verify(queryService).pageCc(eq(3L), eq(10006), eq("100008"), eq(1), eq(20));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("cc:缺 userId → 400,零查询")
|
||||
void ccMissingUserId() throws Exception {
|
||||
mockMvc.perform(get("/oapi/flow/cc").param("schId", "3"))
|
||||
.andExpect(status().is4xxClientError());
|
||||
verify(queryService, never()).pageCc(any(), any(), any(), any(), any());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user