新增e2e测试脚本,修复部分问题
This commit was merged in pull request #51.
This commit is contained in:
+225
@@ -0,0 +1,225 @@
|
||||
package cn.novalon.gym.manage.member.entity;
|
||||
|
||||
import cn.novalon.gym.manage.member.enums.MemberCardRecordStatus;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("实体类单元测试")
|
||||
class MemberEntityTest {
|
||||
|
||||
// ==================== Member ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("Member 应能通过 Builder 构建并正确读写字段")
|
||||
void member_shouldSupportBuilderAndGettersSetters() {
|
||||
Member member = Member.builder()
|
||||
.memberNo("GYMABC12345")
|
||||
.nickname("测试用户")
|
||||
.phone("13812348001")
|
||||
.gender(1)
|
||||
.birthday(LocalDate.of(1995, 6, 15))
|
||||
.address("广东省深圳市")
|
||||
.subscribed(true)
|
||||
.avatar("https://example.com/avatar.png")
|
||||
.unionId("union-id-123")
|
||||
.miniappOpenId("miniapp-open-id-456")
|
||||
.officialOpenId("official-open-id-789")
|
||||
.isDeleted(false)
|
||||
.lastLoginAt(LocalDateTime.of(2026, 7, 1, 12, 0))
|
||||
.build();
|
||||
|
||||
assertThat(member.getMemberNo()).isEqualTo("GYMABC12345");
|
||||
assertThat(member.getNickname()).isEqualTo("测试用户");
|
||||
assertThat(member.getPhone()).isEqualTo("13812348001");
|
||||
assertThat(member.getGender()).isEqualTo(1);
|
||||
assertThat(member.getBirthday()).isEqualTo(LocalDate.of(1995, 6, 15));
|
||||
assertThat(member.getAddress()).isEqualTo("广东省深圳市");
|
||||
assertThat(member.getSubscribed()).isTrue();
|
||||
assertThat(member.getAvatar()).isEqualTo("https://example.com/avatar.png");
|
||||
assertThat(member.getUnionId()).isEqualTo("union-id-123");
|
||||
assertThat(member.getMiniappOpenId()).isEqualTo("miniapp-open-id-456");
|
||||
assertThat(member.getOfficialOpenId()).isEqualTo("official-open-id-789");
|
||||
assertThat(member.getIsDeleted()).isFalse();
|
||||
assertThat(member.getLastLoginAt()).isEqualTo(LocalDateTime.of(2026, 7, 1, 12, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Member setter 应能修改字段值")
|
||||
void member_shouldSupportSetters() {
|
||||
Member member = new Member();
|
||||
member.setMemberNo("GYMNEW001");
|
||||
member.setNickname("新用户");
|
||||
member.setPhone("18987654321");
|
||||
member.setGender(2);
|
||||
|
||||
assertThat(member.getMemberNo()).isEqualTo("GYMNEW001");
|
||||
assertThat(member.getNickname()).isEqualTo("新用户");
|
||||
assertThat(member.getPhone()).isEqualTo("18987654321");
|
||||
assertThat(member.getGender()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Member equals 应基于超类 BaseEntity 逻辑")
|
||||
void member_equals_shouldWork() {
|
||||
Member member1 = Member.builder().memberNo("GYM001").nickname("用户A").build();
|
||||
Member member2 = Member.builder().memberNo("GYM001").nickname("用户A").build();
|
||||
Member member3 = Member.builder().memberNo("GYM002").nickname("用户B").build();
|
||||
|
||||
// @EqualsAndHashCode(callSuper = true) - 基于父类字段
|
||||
// 由于没有设置父类 ID 字段,两个 builder 创建的对象默认应相等
|
||||
assertThat(member1).isEqualTo(member2);
|
||||
// member3 字段不同,应不等(取决于父类 equals 实现)
|
||||
}
|
||||
|
||||
// ==================== MemberCard ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCard 应能通过 Builder 构建并正确读写字段")
|
||||
void memberCard_shouldSupportBuilderAndGettersSetters() {
|
||||
MemberCard card = MemberCard.builder()
|
||||
.memberCardId(1L)
|
||||
.memberCardName("年卡")
|
||||
.memberCardType("TIME_CARD")
|
||||
.memberCardPrice(2999.0)
|
||||
.memberCardValidityDays(365)
|
||||
.memberCardTotalTimes(null)
|
||||
.memberCardAmount(null)
|
||||
.memberCardStatus(1)
|
||||
.build();
|
||||
|
||||
assertThat(card.getMemberCardId()).isEqualTo(1L);
|
||||
assertThat(card.getMemberCardName()).isEqualTo("年卡");
|
||||
assertThat(card.getMemberCardType()).isEqualTo("TIME_CARD");
|
||||
assertThat(card.getMemberCardPrice()).isEqualTo(2999.0);
|
||||
assertThat(card.getMemberCardValidityDays()).isEqualTo(365);
|
||||
assertThat(card.getMemberCardTotalTimes()).isNull();
|
||||
assertThat(card.getMemberCardAmount()).isNull();
|
||||
assertThat(card.getMemberCardStatus()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCard setter 应能修改字段值")
|
||||
void memberCard_shouldSupportSetters() {
|
||||
MemberCard card = new MemberCard();
|
||||
card.setMemberCardName("季卡");
|
||||
card.setMemberCardType("TIME_CARD");
|
||||
card.setMemberCardPrice(999.0);
|
||||
|
||||
assertThat(card.getMemberCardName()).isEqualTo("季卡");
|
||||
assertThat(card.getMemberCardType()).isEqualTo("TIME_CARD");
|
||||
assertThat(card.getMemberCardPrice()).isEqualTo(999.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCard 次卡应有总次数字段")
|
||||
void memberCard_countCard_shouldHaveTotalTimes() {
|
||||
MemberCard card = MemberCard.builder()
|
||||
.memberCardId(2L)
|
||||
.memberCardName("10次卡")
|
||||
.memberCardType("COUNT_CARD")
|
||||
.memberCardPrice(500.0)
|
||||
.memberCardTotalTimes(10)
|
||||
.build();
|
||||
|
||||
assertThat(card.getMemberCardType()).isEqualTo("COUNT_CARD");
|
||||
assertThat(card.getMemberCardTotalTimes()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCard 储值卡应有面额字段")
|
||||
void memberCard_storedValueCard_shouldHaveAmount() {
|
||||
MemberCard card = MemberCard.builder()
|
||||
.memberCardId(3L)
|
||||
.memberCardName("1000元储值卡")
|
||||
.memberCardType("STORED_VALUE_CARD")
|
||||
.memberCardPrice(1000.0)
|
||||
.memberCardAmount(1000.0)
|
||||
.build();
|
||||
|
||||
assertThat(card.getMemberCardType()).isEqualTo("STORED_VALUE_CARD");
|
||||
assertThat(card.getMemberCardAmount()).isEqualTo(1000.0);
|
||||
}
|
||||
|
||||
// ==================== MemberCardRecord ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCardRecord 应能通过 Builder 构建并正确读写字段")
|
||||
void memberCardRecord_shouldSupportBuilderAndGettersSetters() {
|
||||
LocalDateTime now = LocalDateTime.of(2026, 7, 22, 10, 0);
|
||||
MemberCardRecord record = MemberCardRecord.builder()
|
||||
.id(1L)
|
||||
.memberCardRecordId(100L)
|
||||
.memberId(10L)
|
||||
.memberCardId(5L)
|
||||
.status(MemberCardRecordStatus.ACTIVE)
|
||||
.remainingTimes(8)
|
||||
.remainingAmount(200.0)
|
||||
.expireTime(LocalDateTime.of(2027, 7, 22, 0, 0))
|
||||
.sourceOrderId(500L)
|
||||
.purchaseTime(now)
|
||||
.version(0)
|
||||
.memberCardName("10次卡")
|
||||
.memberCardType("COUNT_CARD")
|
||||
.memberCardPrice(500.0)
|
||||
.memberCardValidityDays(30)
|
||||
.memberCardTotalTimes(10)
|
||||
.build();
|
||||
|
||||
assertThat(record.getId()).isEqualTo(1L);
|
||||
assertThat(record.getMemberCardRecordId()).isEqualTo(100L);
|
||||
assertThat(record.getMemberId()).isEqualTo(10L);
|
||||
assertThat(record.getMemberCardId()).isEqualTo(5L);
|
||||
assertThat(record.getStatus()).isEqualTo(MemberCardRecordStatus.ACTIVE);
|
||||
assertThat(record.getRemainingTimes()).isEqualTo(8);
|
||||
assertThat(record.getRemainingAmount()).isEqualTo(200.0);
|
||||
assertThat(record.getExpireTime()).isEqualTo(LocalDateTime.of(2027, 7, 22, 0, 0));
|
||||
assertThat(record.getSourceOrderId()).isEqualTo(500L);
|
||||
assertThat(record.getPurchaseTime()).isEqualTo(now);
|
||||
assertThat(record.getVersion()).isEqualTo(0);
|
||||
assertThat(record.getMemberCardName()).isEqualTo("10次卡");
|
||||
assertThat(record.getMemberCardType()).isEqualTo("COUNT_CARD");
|
||||
assertThat(record.getMemberCardPrice()).isEqualTo(500.0);
|
||||
assertThat(record.getMemberCardValidityDays()).isEqualTo(30);
|
||||
assertThat(record.getMemberCardTotalTimes()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCardRecord setter 应能修改字段值")
|
||||
void memberCardRecord_shouldSupportSetters() {
|
||||
MemberCardRecord record = new MemberCardRecord();
|
||||
record.setMemberCardRecordId(200L);
|
||||
record.setRemainingTimes(5);
|
||||
record.setStatus(MemberCardRecordStatus.USED_UP);
|
||||
|
||||
assertThat(record.getMemberCardRecordId()).isEqualTo(200L);
|
||||
assertThat(record.getRemainingTimes()).isEqualTo(5);
|
||||
assertThat(record.getStatus()).isEqualTo(MemberCardRecordStatus.USED_UP);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCardRecord 状态值应正确:ACTIVE")
|
||||
void memberCardRecord_statusActive_shouldBeCorrect() {
|
||||
MemberCardRecord record = MemberCardRecord.builder()
|
||||
.status(MemberCardRecordStatus.ACTIVE)
|
||||
.build();
|
||||
|
||||
assertThat(record.getStatus()).isEqualTo(MemberCardRecordStatus.ACTIVE);
|
||||
assertThat(record.getStatus().getDesc()).isEqualTo("有效");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCardRecord 状态值应正确:EXPIRED")
|
||||
void memberCardRecord_statusExpired_shouldBeCorrect() {
|
||||
MemberCardRecord record = MemberCardRecord.builder()
|
||||
.status(MemberCardRecordStatus.EXPIRED)
|
||||
.build();
|
||||
|
||||
assertThat(record.getStatus()).isEqualTo(MemberCardRecordStatus.EXPIRED);
|
||||
assertThat(record.getStatus().getDesc()).isEqualTo("过期");
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package cn.novalon.gym.manage.member.enums;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("枚举类单元测试")
|
||||
class MemberEnumsTest {
|
||||
|
||||
// ==================== GenderEnum ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("GenderEnum 应有 UNKNOWN、MALE、FEMALE 三个值")
|
||||
void genderEnum_shouldHaveThreeValues() {
|
||||
GenderEnum[] values = GenderEnum.values();
|
||||
|
||||
assertThat(values).containsExactly(GenderEnum.UNKNOWN, GenderEnum.MALE, GenderEnum.FEMALE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("GenderEnum 各值的 code 和 desc 应正确")
|
||||
void genderEnum_shouldHaveCorrectCodeAndDesc() {
|
||||
assertThat(GenderEnum.UNKNOWN.getCode()).isEqualTo(0);
|
||||
assertThat(GenderEnum.UNKNOWN.getDesc()).isEqualTo("未知");
|
||||
assertThat(GenderEnum.MALE.getCode()).isEqualTo(1);
|
||||
assertThat(GenderEnum.MALE.getDesc()).isEqualTo("男");
|
||||
assertThat(GenderEnum.FEMALE.getCode()).isEqualTo(2);
|
||||
assertThat(GenderEnum.FEMALE.getDesc()).isEqualTo("女");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"0, UNKNOWN",
|
||||
"1, MALE",
|
||||
"2, FEMALE"
|
||||
})
|
||||
@DisplayName("GenderEnum.fromCode 应正确映射")
|
||||
void genderEnum_fromCode_shouldMapCorrectly(int code, GenderEnum expected) {
|
||||
assertThat(GenderEnum.fromCode(code)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("GenderEnum.fromCode(null) 应返回 UNKNOWN")
|
||||
void genderEnum_fromCodeNull_shouldReturnUnknown() {
|
||||
assertThat(GenderEnum.fromCode(null)).isEqualTo(GenderEnum.UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("GenderEnum.fromCode(无效值) 应返回 UNKNOWN")
|
||||
void genderEnum_fromCodeInvalid_shouldReturnUnknown() {
|
||||
assertThat(GenderEnum.fromCode(999)).isEqualTo(GenderEnum.UNKNOWN);
|
||||
}
|
||||
|
||||
// ==================== MemberCardType ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCardType 应有 TIME_CARD、COUNT_CARD、STORED_VALUE_CARD 三个值")
|
||||
void memberCardType_shouldHaveThreeValues() {
|
||||
MemberCardType[] values = MemberCardType.values();
|
||||
|
||||
assertThat(values).containsExactly(
|
||||
MemberCardType.TIME_CARD,
|
||||
MemberCardType.COUNT_CARD,
|
||||
MemberCardType.STORED_VALUE_CARD);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCardType 各值的描述应正确")
|
||||
void memberCardType_shouldHaveCorrectDescriptions() {
|
||||
assertThat(MemberCardType.TIME_CARD.getDesc()).isEqualTo("时长卡");
|
||||
assertThat(MemberCardType.COUNT_CARD.getDesc()).isEqualTo("次卡");
|
||||
assertThat(MemberCardType.STORED_VALUE_CARD.getDesc()).isEqualTo("储值卡");
|
||||
}
|
||||
|
||||
// ==================== MemberCardRecordStatus ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCardRecordStatus 应有 ACTIVE、USED_UP、EXPIRED、REFUNDED 四个值")
|
||||
void memberCardRecordStatus_shouldHaveFourValues() {
|
||||
MemberCardRecordStatus[] values = MemberCardRecordStatus.values();
|
||||
|
||||
assertThat(values).containsExactly(
|
||||
MemberCardRecordStatus.ACTIVE,
|
||||
MemberCardRecordStatus.USED_UP,
|
||||
MemberCardRecordStatus.EXPIRED,
|
||||
MemberCardRecordStatus.REFUNDED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MemberCardRecordStatus 各值的描述应正确")
|
||||
void memberCardRecordStatus_shouldHaveCorrectDescriptions() {
|
||||
assertThat(MemberCardRecordStatus.ACTIVE.getDesc()).isEqualTo("有效");
|
||||
assertThat(MemberCardRecordStatus.USED_UP.getDesc()).isEqualTo("用完");
|
||||
assertThat(MemberCardRecordStatus.EXPIRED.getDesc()).isEqualTo("过期");
|
||||
assertThat(MemberCardRecordStatus.REFUNDED.getDesc()).isEqualTo("已退款");
|
||||
}
|
||||
|
||||
// ==================== CardEvent ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("CardEvent 应包含 ACTIVATE、USE、RENEW、EXPIRE、REFUND、DISABLE 六个事件")
|
||||
void cardEvent_shouldHaveAllEvents() {
|
||||
CardEvent[] values = CardEvent.values();
|
||||
|
||||
assertThat(values).containsExactly(
|
||||
CardEvent.ACTIVATE,
|
||||
CardEvent.USE,
|
||||
CardEvent.RENEW,
|
||||
CardEvent.EXPIRE,
|
||||
CardEvent.REFUND,
|
||||
CardEvent.DISABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("CardEvent 各事件的描述应正确")
|
||||
void cardEvent_shouldHaveCorrectDescriptions() {
|
||||
assertThat(CardEvent.ACTIVATE.getDesc()).isEqualTo("激活卡片");
|
||||
assertThat(CardEvent.USE.getDesc()).isEqualTo("使用卡片");
|
||||
assertThat(CardEvent.RENEW.getDesc()).isEqualTo("续费");
|
||||
assertThat(CardEvent.EXPIRE.getDesc()).isEqualTo("过期");
|
||||
assertThat(CardEvent.REFUND.getDesc()).isEqualTo("退款");
|
||||
assertThat(CardEvent.DISABLE.getDesc()).isEqualTo("禁用");
|
||||
}
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
package cn.novalon.gym.manage.member.handler;
|
||||
|
||||
import cn.novalon.gym.manage.member.entity.MemberCardRecord;
|
||||
import cn.novalon.gym.manage.member.enums.CardEvent;
|
||||
import cn.novalon.gym.manage.member.enums.MemberCardRecordStatus;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
@DisplayName("MemberCardStateMachine 单元测试")
|
||||
class MemberCardStateMachineTest {
|
||||
|
||||
private MemberCardStateMachine stateMachine;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
stateMachine = new MemberCardStateMachine();
|
||||
}
|
||||
|
||||
// ==================== canTransition 测试 ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("ACTIVE + USE 应可以转换(保持 ACTIVE)")
|
||||
void canTransition_activeToActiveViaUse_shouldReturnTrue() {
|
||||
Boolean result = stateMachine.canTransition(MemberCardRecordStatus.ACTIVE, CardEvent.USE).block();
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("ACTIVE + EXPIRE 应可以转换到 EXPIRED")
|
||||
void canTransition_activeToExpiredViaExpire_shouldReturnTrue() {
|
||||
Boolean result = stateMachine.canTransition(MemberCardRecordStatus.ACTIVE, CardEvent.EXPIRE).block();
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("ACTIVE + REFUND 应可以转换到 REFUNDED")
|
||||
void canTransition_activeToRefundedViaRefund_shouldReturnTrue() {
|
||||
Boolean result = stateMachine.canTransition(MemberCardRecordStatus.ACTIVE, CardEvent.REFUND).block();
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("ACTIVE + RENEW 应可以转换(保持 ACTIVE)")
|
||||
void canTransition_activeToActiveViaRenew_shouldReturnTrue() {
|
||||
Boolean result = stateMachine.canTransition(MemberCardRecordStatus.ACTIVE, CardEvent.RENEW).block();
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("REFUNDED + 任何事件都不应可以转换")
|
||||
void canTransition_refundedWithAnyEvent_shouldReturnFalse() {
|
||||
for (CardEvent event : CardEvent.values()) {
|
||||
Boolean result = stateMachine.canTransition(MemberCardRecordStatus.REFUNDED, event).block();
|
||||
assertThat(result).as("REFUNDED + %s", event).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("USED_UP + USE 不应可以转换")
|
||||
void canTransition_usedUpViaUse_shouldReturnFalse() {
|
||||
Boolean result = stateMachine.canTransition(MemberCardRecordStatus.USED_UP, CardEvent.USE).block();
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("USED_UP + EXPIRE 不应可以转换")
|
||||
void canTransition_usedUpViaExpire_shouldReturnFalse() {
|
||||
Boolean result = stateMachine.canTransition(MemberCardRecordStatus.USED_UP, CardEvent.EXPIRE).block();
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("EXPIRED + USE 不应可以转换")
|
||||
void canTransition_expiredViaUse_shouldReturnFalse() {
|
||||
Boolean result = stateMachine.canTransition(MemberCardRecordStatus.EXPIRED, CardEvent.USE).block();
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("EXPIRED + REFUND 不应可以转换")
|
||||
void canTransition_expiredViaRefund_shouldReturnFalse() {
|
||||
Boolean result = stateMachine.canTransition(MemberCardRecordStatus.EXPIRED, CardEvent.REFUND).block();
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
// ==================== transition 测试 ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("ACTIVE + EXPIRE 转换后状态应为 EXPIRED")
|
||||
void transition_activeExpire_shouldReturnExpired() {
|
||||
MemberCardRecordStatus result = stateMachine.transition(
|
||||
MemberCardRecordStatus.ACTIVE, CardEvent.EXPIRE).block();
|
||||
|
||||
assertThat(result).isEqualTo(MemberCardRecordStatus.EXPIRED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("ACTIVE + REFUND 转换后状态应为 REFUNDED")
|
||||
void transition_activeRefund_shouldReturnRefunded() {
|
||||
MemberCardRecordStatus result = stateMachine.transition(
|
||||
MemberCardRecordStatus.ACTIVE, CardEvent.REFUND).block();
|
||||
|
||||
assertThat(result).isEqualTo(MemberCardRecordStatus.REFUNDED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("USED_UP + RENEW 转换后状态应为 ACTIVE")
|
||||
void transition_usedUpRenew_shouldReturnActive() {
|
||||
MemberCardRecordStatus result = stateMachine.transition(
|
||||
MemberCardRecordStatus.USED_UP, CardEvent.RENEW).block();
|
||||
|
||||
assertThat(result).isEqualTo(MemberCardRecordStatus.ACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("EXPIRED + RENEW 转换后状态应为 ACTIVE")
|
||||
void transition_expiredRenew_shouldReturnActive() {
|
||||
MemberCardRecordStatus result = stateMachine.transition(
|
||||
MemberCardRecordStatus.EXPIRED, CardEvent.RENEW).block();
|
||||
|
||||
assertThat(result).isEqualTo(MemberCardRecordStatus.ACTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("无效转换应抛出 IllegalStateException")
|
||||
void transition_invalidTransition_shouldThrowIllegalStateException() {
|
||||
assertThatThrownBy(() -> stateMachine.transition(
|
||||
MemberCardRecordStatus.REFUNDED, CardEvent.USE).block())
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessageContaining("不允许的状态转换");
|
||||
}
|
||||
|
||||
// ==================== validateTransition 测试 ====================
|
||||
|
||||
@Test
|
||||
@DisplayName("合法转换的 validateTransition 应正常完成")
|
||||
void validateTransition_validTransition_shouldCompleteSuccessfully() {
|
||||
MemberCardRecord card = MemberCardRecord.builder()
|
||||
.memberCardRecordId(1L)
|
||||
.status(MemberCardRecordStatus.ACTIVE)
|
||||
.build();
|
||||
|
||||
// 不应抛异常,Mono<Void> 正常完成
|
||||
stateMachine.validateTransition(card, CardEvent.USE).block();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("非法转换的 validateTransition 应抛出 IllegalStateException")
|
||||
void validateTransition_invalidTransition_shouldThrowIllegalStateException() {
|
||||
MemberCardRecord card = MemberCardRecord.builder()
|
||||
.memberCardRecordId(100L)
|
||||
.status(MemberCardRecordStatus.REFUNDED)
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> stateMachine.validateTransition(card, CardEvent.USE).block())
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessageContaining("不允许的状态转换")
|
||||
.hasMessageContaining("会员卡记录ID=")
|
||||
.hasMessageContaining("100");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("USED_UP + USE 转换验证应失败")
|
||||
void validateTransition_usedUpUse_shouldFail() {
|
||||
MemberCardRecord card = MemberCardRecord.builder()
|
||||
.memberCardRecordId(2L)
|
||||
.status(MemberCardRecordStatus.USED_UP)
|
||||
.build();
|
||||
|
||||
assertThatThrownBy(() -> stateMachine.validateTransition(card, CardEvent.USE).block())
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessageContaining("不允许的状态转换");
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package cn.novalon.gym.manage.member.util;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
@DisplayName("AesUtil 单元测试")
|
||||
class AesUtilTest {
|
||||
|
||||
private static final String TEST_KEY = Base64.getEncoder().encodeToString(
|
||||
"1234567890123456".getBytes(StandardCharsets.UTF_8));
|
||||
private static final String TEST_IV = Base64.getEncoder().encodeToString(
|
||||
"abcdefghijklmnop".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() throws Exception {
|
||||
setStaticField("KEY", TEST_KEY);
|
||||
setStaticField("IV", TEST_IV);
|
||||
}
|
||||
|
||||
private static void setStaticField(String fieldName, String value) throws Exception {
|
||||
Field field = AesUtil.class.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
field.set(null, value);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("加密手机号,结果不为 null 且与原文不同")
|
||||
void encrypt_shouldReturnNonNullAndDifferentFromOriginal() {
|
||||
String plainText = "13812348001";
|
||||
|
||||
String encrypted = AesUtil.encrypt(plainText);
|
||||
|
||||
assertThat(encrypted).isNotNull();
|
||||
assertThat(encrypted).isNotEqualTo(plainText);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("解密加密后的手机号,应与原文一致")
|
||||
void decrypt_shouldReturnOriginalText() {
|
||||
String plainText = "13812348001";
|
||||
String encrypted = AesUtil.encrypt(plainText);
|
||||
|
||||
String decrypted = AesUtil.decrypt(encrypted);
|
||||
|
||||
assertThat(decrypted).isEqualTo(plainText);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("加密再解密应可逆")
|
||||
void encryptAndDecrypt_shouldBeReversible() {
|
||||
String[] testData = {
|
||||
"13812348001",
|
||||
"HelloWorld",
|
||||
"测试中文",
|
||||
"!@#$%^&*()"
|
||||
};
|
||||
|
||||
for (String original : testData) {
|
||||
String encrypted = AesUtil.encrypt(original);
|
||||
String decrypted = AesUtil.decrypt(encrypted);
|
||||
|
||||
assertThat(decrypted).as("原文: %s", original).isEqualTo(original);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("加密 null 应抛出异常")
|
||||
void encrypt_withNull_shouldThrowException() {
|
||||
assertThatThrownBy(() -> AesUtil.encrypt(null))
|
||||
.isInstanceOf(RuntimeException.class)
|
||||
.hasMessageContaining("加密失败");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("加密空字符串不应抛出异常")
|
||||
void encrypt_withEmptyString_shouldNotThrow() {
|
||||
String encrypted = AesUtil.encrypt("");
|
||||
|
||||
assertThat(encrypted).isNotNull();
|
||||
assertThat(encrypted).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("解密无效的 Base64 字符串应抛异常")
|
||||
void decrypt_withInvalidBase64_shouldThrowException() {
|
||||
assertThatThrownBy(() -> AesUtil.decrypt("not-valid-base64!!!"))
|
||||
.isInstanceOf(RuntimeException.class)
|
||||
.hasMessageContaining("解密失败");
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package cn.novalon.gym.manage.member.util;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("MemberNoGenerator 单元测试")
|
||||
class MemberNoGeneratorTest {
|
||||
|
||||
private static final String PREFIX = "GYM";
|
||||
private static final int EXPECTED_LENGTH = 11; // PREFIX(3) + RANDOM(8)
|
||||
private static final String VALID_CHARS = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
||||
|
||||
@Test
|
||||
@DisplayName("生成会员号,应不为 null 且不为空")
|
||||
void generate_shouldReturnNonNullAndNonEmpty() {
|
||||
String memberNo = MemberNoGenerator.generate();
|
||||
|
||||
assertThat(memberNo).isNotNull();
|
||||
assertThat(memberNo).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("会员号应以 GYM 前缀开头")
|
||||
void generate_shouldStartWithGymPrefix() {
|
||||
String memberNo = MemberNoGenerator.generate();
|
||||
|
||||
assertThat(memberNo).startsWith(PREFIX);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("会员号长度应为 11")
|
||||
void generate_shouldHaveCorrectLength() {
|
||||
String memberNo = MemberNoGenerator.generate();
|
||||
|
||||
assertThat(memberNo).hasSize(EXPECTED_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("会员号随机部分应只包含合法字符")
|
||||
void generate_shouldOnlyContainValidCharacters() {
|
||||
String memberNo = MemberNoGenerator.generate();
|
||||
String randomPart = memberNo.substring(PREFIX.length());
|
||||
|
||||
for (char c : randomPart.toCharArray()) {
|
||||
assertThat(VALID_CHARS.indexOf(c))
|
||||
.as("字符 '%c' 不在合法字符集中", c)
|
||||
.isGreaterThanOrEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("批量生成会员号,应所有值均唯一")
|
||||
void generateBatch_shouldProduceUniqueValues() {
|
||||
int count = 100;
|
||||
|
||||
String[] memberNos = MemberNoGenerator.generateBatch(count);
|
||||
|
||||
assertThat(memberNos).hasSize(count);
|
||||
Set<String> uniqueSet = new HashSet<>(Arrays.asList(memberNos));
|
||||
assertThat(uniqueSet).hasSize(count);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("生成多个会员号应不同")
|
||||
void generate_multiple_shouldBeDifferent() {
|
||||
String no1 = MemberNoGenerator.generate();
|
||||
String no2 = MemberNoGenerator.generate();
|
||||
String no3 = MemberNoGenerator.generate();
|
||||
|
||||
Set<String> set = new HashSet<>();
|
||||
set.add(no1);
|
||||
set.add(no2);
|
||||
set.add(no3);
|
||||
|
||||
assertThat(set).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("会员号格式应符合 GYM + 8位大写字母数字")
|
||||
void generate_shouldMatchExpectedFormat() {
|
||||
String memberNo = MemberNoGenerator.generate();
|
||||
|
||||
// 格式:GYM + 8位大写字母或数字(排除0/O/1/I/l)
|
||||
assertThat(memberNo).matches("GYM[23456789ABCDEFGHJKLMNPQRSTUVWXYZ]{8}");
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package cn.novalon.gym.manage.member.util;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("WechatPhoneUtil 单元测试")
|
||||
class WechatPhoneUtilTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("11位手机号应中间4位脱敏")
|
||||
void maskPhone_11digit_shouldMaskMiddle4Digits() {
|
||||
String result = WechatPhoneUtil.maskPhone("13812348001");
|
||||
|
||||
assertThat(result).isEqualTo("138****8001");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("另一个11位手机号应正确脱敏")
|
||||
void maskPhone_another11digit_shouldMaskCorrectly() {
|
||||
String result = WechatPhoneUtil.maskPhone("18987654321");
|
||||
|
||||
assertThat(result).isEqualTo("189****4321");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("带国家代码的手机号应脱敏")
|
||||
void maskPhone_withCountryCode_shouldMaskCorrectly() {
|
||||
// 带国家代码:+8613812348001,共14位
|
||||
String result = WechatPhoneUtil.maskPhone("+8613812348001");
|
||||
|
||||
assertThat(result).isEqualTo("+86****2348001");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("不足7位的短号码应返回 ***")
|
||||
void maskPhone_shortNumber_shouldReturnAsterisks() {
|
||||
assertThat(WechatPhoneUtil.maskPhone("123456"))
|
||||
.isEqualTo("***");
|
||||
assertThat(WechatPhoneUtil.maskPhone("1"))
|
||||
.isEqualTo("***");
|
||||
assertThat(WechatPhoneUtil.maskPhone(""))
|
||||
.isEqualTo("***");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("null 输入应返回 ***")
|
||||
void maskPhone_nullInput_shouldReturnAsterisks() {
|
||||
assertThat(WechatPhoneUtil.maskPhone(null))
|
||||
.isEqualTo("***");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("正好7位的号码应能脱敏")
|
||||
void maskPhone_exactly7digits_shouldMask() {
|
||||
// 正好7位,前3位 + 4个星号 + 后4位... 但只有7位,从索引7开始取0个字符
|
||||
// "1234567" → substring(0,3) + "****" + substring(7) = "123****"
|
||||
String result = WechatPhoneUtil.maskPhone("1234567");
|
||||
|
||||
assertThat(result).isEqualTo("123****");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user