test: 添加微信端优惠券、订单评价、订单管理和提现的API测试
Some checks failed
Build and Push Backend / docker (push) Failing after 5s

新增四个API集成测试类:
- WxCouponControllerApiTest: 测试优惠券领取、查询、使用限制和白名单逻辑
- WxCustomOrderEvaluationApiTest: 测试订单评价创建和查询功能
- WxOrderInfoControllerApiTest: 测试随机订单接单、续单申请和隐私字段处理
- WxWithdrawControllerApiTest: 测试收益余额查询、提现申请和收益明细过滤

提高微信端核心业务流程的测试覆盖率
This commit is contained in:
irving
2025-11-02 10:32:16 -05:00
parent 7b9f1fd8c2
commit 79b516d81c
4 changed files with 999 additions and 0 deletions

View File

@@ -0,0 +1,220 @@
package com.starry.admin.api;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.reset;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.starry.admin.common.apitest.ApiTestDataSeeder;
import com.starry.admin.common.task.OverdueOrderHandlerTask;
import com.starry.admin.modules.order.module.constant.OrderConstant;
import com.starry.admin.modules.order.module.entity.PlayOrderContinueInfoEntity;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.order.service.IPlayOrderContinueInfoService;
import com.starry.admin.modules.weichat.service.NotificationSender;
import com.starry.admin.modules.weichat.service.WxCustomMpService;
import com.starry.common.constant.Constants;
import com.starry.common.context.CustomSecurityContextHolder;
import java.time.LocalDateTime;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
class WxOrderInfoControllerApiTest extends WxCustomOrderApiTestSupport {
private static final String MESSAGE_OPERATION_SUCCESS = "操作成功";
private static final String REVIEW_PENDING_STATE = "0";
private static final String EXCLUDE_HISTORY_DISABLED = "0";
private static final String OTHER_CLERK_ID = "clerk-apitest-other";
private static final int SINGLE_QUANTITY = 1;
@MockBean
private NotificationSender notificationSender;
@MockBean
private WxCustomMpService wxCustomMpService;
@MockBean
private OverdueOrderHandlerTask overdueOrderHandlerTask;
@Autowired
private IPlayOrderContinueInfoService orderContinueInfoService;
private final ObjectMapper mapper = new ObjectMapper();
private String customerToken;
private String clerkToken;
@BeforeEach
void setUpTokens() {
ensureTenantContext();
customerToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID);
customUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID, customerToken);
clerkToken = wxTokenService.createWxUserToken(ApiTestDataSeeder.DEFAULT_CLERK_ID);
clerkUserInfoService.updateTokenById(ApiTestDataSeeder.DEFAULT_CLERK_ID, clerkToken);
// Relax notifications to avoid strict verification noise
doNothing().when(notificationSender).sendOrderMessageAsync(Mockito.any());
doNothing().when(notificationSender).sendOrderFinishMessageAsync(Mockito.any());
doNothing().when(wxCustomMpService).sendCreateOrderMessageBatch(anyList(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
doNothing().when(overdueOrderHandlerTask).enqueue(Mockito.anyString());
}
@AfterEach
void tearDown() {
ensureTenantContext();
orderContinueInfoService.lambdaUpdate()
.eq(PlayOrderContinueInfoEntity::getClerkId, ApiTestDataSeeder.DEFAULT_CLERK_ID)
.remove();
reset(notificationSender, wxCustomMpService, overdueOrderHandlerTask);
CustomSecurityContextHolder.remove();
}
@Test
void selectRandomOrderByIdHidesCustomerContactWhenPending() throws Exception {
String marker = "privacy-" + LocalDateTime.now().toString();
String orderId = createRandomOrder(marker);
MvcResult result = mockMvc.perform(get("/wx/order/clerk/selectRandomOrderById")
.param("id", orderId)
.header(USER_HEADER, DEFAULT_USER)
.header(TENANT_HEADER, DEFAULT_TENANT)
.header(Constants.CLERK_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + clerkToken))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200))
.andReturn();
JsonNode data = mapper.readTree(result.getResponse().getContentAsString()).path("data");
assertThat(data.path("weiChatCode").asText()).isEmpty();
assertThat(data.path("placeType").asText()).isEqualTo(OrderConstant.PlaceType.RANDOM.getCode());
assertThat(data.path("orderStatus").asText()).isEqualTo(OrderConstant.OrderStatus.PENDING.getCode());
}
@Test
void duplicateContinuationRequestIsRejected() throws Exception {
String marker = "continue-" + LocalDateTime.now().toString();
String orderId = createRandomOrder(marker);
acceptOrder(orderId);
ArrayNode images = mapper.createArrayNode().add("https://example.com/proof.png");
ObjectNode payload = mapper.createObjectNode()
.put("orderId", orderId)
.put("remark", "加场申请")
.set("images", images);
mockMvc.perform(post("/wx/order/clerk/continue")
.header(USER_HEADER, DEFAULT_USER)
.header(TENANT_HEADER, DEFAULT_TENANT)
.header(Constants.CLERK_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + clerkToken)
.contentType(MediaType.APPLICATION_JSON)
.content(payload.toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data").value("下单成功"))
.andExpect(jsonPath("$.message").value(MESSAGE_OPERATION_SUCCESS));
mockMvc.perform(post("/wx/order/clerk/continue")
.header(USER_HEADER, DEFAULT_USER)
.header(TENANT_HEADER, DEFAULT_TENANT)
.header(Constants.CLERK_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + clerkToken)
.contentType(MediaType.APPLICATION_JSON)
.content(payload.toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(500))
.andExpect(jsonPath("$.message").value("同一场订单只能续单一次"));
ensureTenantContext();
PlayOrderContinueInfoEntity continuation = orderContinueInfoService.lambdaQuery()
.eq(PlayOrderContinueInfoEntity::getOrderId, orderId)
.last("limit 1")
.one();
assertThat(continuation).isNotNull();
assertThat(continuation.getReviewedState()).isEqualTo(REVIEW_PENDING_STATE);
}
@Test
void randomOrderAcceptedByAnotherClerkHidesSensitiveFields() throws Exception {
String marker = "privacy-accepted-" + LocalDateTime.now();
String orderId = createRandomOrder(marker);
ensureTenantContext();
playOrderInfoService.lambdaUpdate()
.eq(PlayOrderInfoEntity::getId, orderId)
.set(PlayOrderInfoEntity::getAcceptBy, OTHER_CLERK_ID)
.set(PlayOrderInfoEntity::getOrderStatus, OrderConstant.OrderStatus.ACCEPTED.getCode())
.update();
MvcResult result = mockMvc.perform(get("/wx/order/clerk/selectRandomOrderById")
.param("id", orderId)
.header(USER_HEADER, DEFAULT_USER)
.header(TENANT_HEADER, DEFAULT_TENANT)
.header(Constants.CLERK_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + clerkToken))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200))
.andReturn();
JsonNode data = mapper.readTree(result.getResponse().getContentAsString()).path("data");
assertThat(data.path("acceptBy").asText()).isEqualTo(OTHER_CLERK_ID);
assertThat(data.path("weiChatCode").asText()).isEmpty();
assertThat(data.path("customId").asText()).isEmpty();
String nickname = data.path("customNickname").asText();
assertThat(nickname.equals("匿名用户") || "匿名用户".equals(nickname)).isTrue();
assertThat(data.path("customAvatar").asText()).isEmpty();
}
private String createRandomOrder(String remark) throws Exception {
ensureTenantContext();
resetCustomerBalance();
ObjectNode payload = mapper.createObjectNode();
payload.put("sex", OrderConstant.Gender.FEMALE.getCode());
payload.put("levelId", ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID);
payload.put("commodityId", ApiTestDataSeeder.DEFAULT_COMMODITY_ID);
payload.put("commodityQuantity", SINGLE_QUANTITY);
payload.put("weiChatCode", "apitest-customer-wx");
payload.put("excludeHistory", EXCLUDE_HISTORY_DISABLED);
payload.set("couponIds", mapper.createArrayNode());
payload.put("remark", remark);
mockMvc.perform(post("/wx/custom/order/random")
.header(USER_HEADER, DEFAULT_USER)
.header(TENANT_HEADER, DEFAULT_TENANT)
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
.contentType(MediaType.APPLICATION_JSON)
.content(payload.toString()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200));
ensureTenantContext();
PlayOrderInfoEntity order = playOrderInfoService.lambdaQuery()
.eq(PlayOrderInfoEntity::getPurchaserBy, ApiTestDataSeeder.DEFAULT_CUSTOMER_ID)
.eq(PlayOrderInfoEntity::getRemark, remark)
.orderByDesc(PlayOrderInfoEntity::getCreatedTime)
.last("limit 1")
.one();
assertThat(order).isNotNull();
return order.getId();
}
private void acceptOrder(String orderId) throws Exception {
mockMvc.perform(get("/wx/clerk/order/accept")
.param("id", orderId)
.header(USER_HEADER, DEFAULT_USER)
.header(TENANT_HEADER, DEFAULT_TENANT)
.header(Constants.CLERK_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + clerkToken))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200));
}
}