test: add wechat integration test suite
Some checks failed
Build and Push Backend / docker (push) Has been cancelled

- Add llm/wechat-subsystem-test-matrix.md and tests covering Wx controllers/services\n- Make ApiTestDataSeeder personnel group seeding idempotent for full-suite stability
This commit is contained in:
irving
2026-01-12 18:54:14 -05:00
parent 56239450d4
commit 985b35cd90
36 changed files with 5293 additions and 48 deletions

View File

@@ -102,7 +102,7 @@ class WxCouponControllerApiTest extends AbstractApiTest {
}
@Test
void obtainCouponRejectsNonWhitelistCustomer() throws Exception {
void obtainCouponReturnsNonEligibilityReasonWhenNonWhitelisted__covers_CP_002() throws Exception {
ensureTenantContext();
String couponId = newCouponId("whitelist");
PlayCouponInfoEntity coupon = createBaseCoupon(couponId);
@@ -123,7 +123,7 @@ class WxCouponControllerApiTest extends AbstractApiTest {
}
@Test
void queryAllSkipsOfflineCouponsAndMarksObtainedOnes() throws Exception {
void queryAllSkipsOfflineCouponsAndMarksObtainedOnes__covers_CP_004() throws Exception {
ensureTenantContext();
PlayCouponInfoEntity onlineCoupon = createBaseCoupon(newCouponId("online"));
onlineCoupon.setCustomWhitelist(List.of(ApiTestDataSeeder.DEFAULT_CUSTOMER_ID));
@@ -164,7 +164,7 @@ class WxCouponControllerApiTest extends AbstractApiTest {
}
@Test
void queryByOrderFlagsOnlyEligibleCouponsAsAvailable() throws Exception {
void queryByOrderFlagsOnlyEligibleCouponsAsAvailable__covers_CP_006() throws Exception {
ensureTenantContext();
PlayCouponInfoEntity eligible = createBaseCoupon(newCouponId("eligible"));
eligible.setUseMinAmount(MINIMUM_USAGE_AMOUNT);
@@ -200,23 +200,30 @@ class WxCouponControllerApiTest extends AbstractApiTest {
.andExpect(jsonPath("$.code").value(200))
.andReturn();
ArrayNode data = (ArrayNode) mapper.readTree(result.getResponse().getContentAsString()).path("data");
ArrayNode data = (ArrayNode) mapper.readTree(result.getResponse().getContentAsString(java.nio.charset.StandardCharsets.UTF_8))
.path("data");
assertThat(data).isNotNull();
assertThat(data.size()).isGreaterThanOrEqualTo(2);
boolean foundEligibleDetail = false;
boolean foundIneligibleDetail = false;
for (JsonNode node : data) {
if (eligible.getId().equals(node.path("couponId").asText())) {
if (eligibleDetail.getId().equals(node.path("id").asText())) {
foundEligibleDetail = true;
assertThat(node.path("available").asText()).isEqualTo("1");
assertThat(node.path("reasonForUnavailableUse").asText("")).isEmpty();
}
if (ineligible.getId().equals(node.path("couponId").asText())) {
if (ineligibleDetail.getId().equals(node.path("id").asText())) {
foundIneligibleDetail = true;
assertThat(node.path("available").asText()).isEqualTo("0");
assertThat(node.path("reasonForUnavailableUse").asText()).isEqualTo("订单类型不符合");
}
}
assertThat(foundEligibleDetail).isTrue();
assertThat(foundIneligibleDetail).isTrue();
}
@Test
void obtainCouponSucceedsWhenEligible() throws Exception {
void obtainCouponSucceedsWhenEligible__covers_CP_003() throws Exception {
ensureTenantContext();
PlayCouponInfoEntity coupon = createBaseCoupon(newCouponId("success"));
couponInfoService.save(coupon);
@@ -244,7 +251,7 @@ class WxCouponControllerApiTest extends AbstractApiTest {
}
@Test
void obtainCouponHonorsPerUserLimit() throws Exception {
void obtainCouponHonorsPerUserLimit__covers_CP_002() throws Exception {
ensureTenantContext();
PlayCouponInfoEntity coupon = createBaseCoupon(newCouponId("limit"));
coupon.setClerkObtainedMaxQuantity(1);
@@ -279,6 +286,121 @@ class WxCouponControllerApiTest extends AbstractApiTest {
.andExpect(jsonPath("$.data.msg").value("优惠券已达到领取上限"));
}
@Test
void obtainCouponRejectsEmptyId__covers_CP_001() throws Exception {
ensureTenantContext();
mockMvc.perform(get("/wx/coupon/custom/obtainCoupon")
.param("id", "")
.header(USER_HEADER, DEFAULT_USER)
.header(TENANT_HEADER, DEFAULT_TENANT)
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(500))
.andExpect(jsonPath("$.message").value("请求参数异常,优惠券ID不能为"));
}
@Test
void queryAllHidesWhitelistCouponFromNonWhitelistCustomer__covers_CP_004() throws Exception {
ensureTenantContext();
String couponId = newCouponId("qall-wl");
PlayCouponInfoEntity coupon = createBaseCoupon(couponId);
coupon.setClaimConditionType(CouponClaimConditionType.WHITELIST.code());
coupon.setCustomWhitelist(List.of("other-customer"));
couponInfoService.save(coupon);
couponIds.add(coupon.getId());
MvcResult result = mockMvc.perform(post("/wx/coupon/custom/queryAll")
.header(USER_HEADER, DEFAULT_USER)
.header(TENANT_HEADER, DEFAULT_TENANT)
.header(Constants.CUSTOM_USER_LOGIN_TOKEN, Constants.TOKEN_PREFIX + customerToken)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200))
.andReturn();
JsonNode data = mapper.readTree(result.getResponse().getContentAsString(java.nio.charset.StandardCharsets.UTF_8))
.path("data");
assertThat(data).isNotNull();
assertThat(data.isArray()).isTrue();
for (JsonNode node : data) {
assertThat(node.path("id").asText()).isNotEqualTo(coupon.getId());
}
}
@Test
void queryByOrderRejectsWhenClerkIdAndLevelIdBothEmpty__covers_CP_005() throws Exception {
ensureTenantContext();
ObjectNode payload = mapper.createObjectNode();
payload.put("commodityId", ApiTestDataSeeder.DEFAULT_COMMODITY_ID);
payload.put("levelId", "");
payload.put("clerkId", "");
payload.put("placeType", OrderConstant.PlaceType.RANDOM.getCode());
payload.put("commodityQuantity", 1);
mockMvc.perform(post("/wx/coupon/custom/queryByOrder")
.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(500))
.andExpect(jsonPath("$.message").value("请求参数异常,店员ID不能为空,等级ID不能为空"));
}
@Test
void queryByOrderSwallowsBrokenCouponAndReturnsRemaining__covers_CP_007() throws Exception {
ensureTenantContext();
PlayCouponInfoEntity eligible = createBaseCoupon(newCouponId("swv"));
eligible.setUseMinAmount(BigDecimal.ZERO);
couponInfoService.save(eligible);
couponIds.add(eligible.getId());
PlayCouponDetailsEntity eligibleDetail = createCouponDetail(eligible.getId(), CouponUseState.UNUSED);
couponDetailsService.save(eligibleDetail);
couponDetailIds.add(eligibleDetail.getId());
PlayCouponDetailsEntity brokenDetail = createCouponDetail(newCouponId("missing"), CouponUseState.UNUSED);
couponDetailsService.save(brokenDetail);
couponDetailIds.add(brokenDetail.getId());
ObjectNode payload = mapper.createObjectNode();
payload.put("commodityId", ApiTestDataSeeder.DEFAULT_COMMODITY_ID);
payload.put("levelId", ApiTestDataSeeder.DEFAULT_CLERK_LEVEL_ID);
payload.put("clerkId", "");
payload.put("placeType", OrderConstant.PlaceType.RANDOM.getCode());
payload.put("commodityQuantity", 1);
MvcResult result = mockMvc.perform(post("/wx/coupon/custom/queryByOrder")
.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))
.andReturn();
JsonNode data = mapper.readTree(result.getResponse().getContentAsString()).path("data");
assertThat(data.isArray()).isTrue();
boolean foundEligibleDetail = false;
boolean foundBrokenDetail = false;
for (JsonNode node : data) {
String returnedId = node.path("id").asText();
if (eligibleDetail.getId().equals(returnedId)) {
foundEligibleDetail = true;
}
if (brokenDetail.getId().equals(returnedId)) {
foundBrokenDetail = true;
}
}
assertThat(foundEligibleDetail).isTrue();
assertThat(foundBrokenDetail).isFalse();
}
private PlayCouponInfoEntity createBaseCoupon(String id) {
PlayCouponInfoEntity coupon = new PlayCouponInfoEntity();
coupon.setId(id);