新增e2e测试脚本,修复部分问题

This commit was merged in pull request #51.
This commit is contained in:
2026-07-22 20:00:13 +08:00
parent 53d1ce6fb2
commit 4c07ec5455
105 changed files with 21700 additions and 318 deletions
@@ -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("过期");
}
}
@@ -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("禁用");
}
}
@@ -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("不允许的状态转换");
}
}
@@ -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("解密失败");
}
}
@@ -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}");
}
}
@@ -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****");
}
}