test: ga-common 유틸 + ga-batch CommissionCalculator 단위 테스트 39건

- MoneyUtilTest (10): pct/tax/mul/div HALF_UP, null 안전성
- DateUtilTest (10): 정산월 범위, 윤년/평년, 월 차이/가산
- MaskUtilTest (7): NAME/PHONE/RRN/ACCOUNT/EMAIL 마스킹 규칙
- EncryptUtilTest (6): AES-256 round-trip, 한글, 짧은 키 패딩
- CommissionCalculatorTest (6): 모집/유지수수료 계산 공식, 폴백, agent 누락

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
GA Pro
2026-05-10 20:47:47 +09:00
parent aee9c2c2ca
commit 8a0483cccc
5 changed files with 487 additions and 0 deletions
@@ -0,0 +1,92 @@
package com.ga.common.util;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
class DateUtilTest {
@Test
@DisplayName("getMonthRange: 평월")
void monthRange_normal() {
String[] r = DateUtil.getMonthRange("202605");
assertThat(r).containsExactly("2026-05-01", "2026-05-31");
}
@Test
@DisplayName("getMonthRange: 윤년 2월")
void monthRange_leap() {
String[] r = DateUtil.getMonthRange("202402");
assertThat(r).containsExactly("2024-02-01", "2024-02-29");
}
@Test
@DisplayName("getMonthRange: 평년 2월")
void monthRange_normalFeb() {
String[] r = DateUtil.getMonthRange("202302");
assertThat(r).containsExactly("2023-02-01", "2023-02-28");
}
@Test
@DisplayName("calcMonthDiff: yyyymm 문자열")
void monthDiff_yyyymm() {
assertThat(DateUtil.calcMonthDiff("202401", "202405")).isEqualTo(4);
assertThat(DateUtil.calcMonthDiff("202312", "202401")).isEqualTo(1);
assertThat(DateUtil.calcMonthDiff("202405", "202401")).isEqualTo(-4);
assertThat(DateUtil.calcMonthDiff("202401", "202401")).isZero();
}
@Test
@DisplayName("calcMonthDiff: LocalDate")
void monthDiff_date() {
// 같은 달의 다른 일자는 0개월
assertThat(DateUtil.calcMonthDiff(LocalDate.of(2024, 1, 1), LocalDate.of(2024, 1, 31))).isZero();
assertThat(DateUtil.calcMonthDiff(LocalDate.of(2024, 1, 31), LocalDate.of(2024, 2, 1))).isEqualTo(1);
assertThat(DateUtil.calcMonthDiff(LocalDate.of(2023, 1, 1), LocalDate.of(2024, 1, 1))).isEqualTo(12);
}
@Test
@DisplayName("addMonth: 월 가산/연도 넘김")
void addMonth() {
assertThat(DateUtil.addMonth("202401", 3)).isEqualTo("202404");
assertThat(DateUtil.addMonth("202312", 1)).isEqualTo("202401");
assertThat(DateUtil.addMonth("202403", -3)).isEqualTo("202312");
}
@Test
@DisplayName("parseDate: 패턴 일치/실패")
void parseDate() {
assertThat(DateUtil.parseDate("2026-05-10", "yyyy-MM-dd")).isEqualTo(LocalDate.of(2026, 5, 10));
assertThat(DateUtil.parseDate("20260510", "yyyyMMdd")).isEqualTo(LocalDate.of(2026, 5, 10));
assertThat(DateUtil.parseDate(null, "yyyy-MM-dd")).isNull();
assertThat(DateUtil.parseDate("", "yyyy-MM-dd")).isNull();
assertThat(DateUtil.parseDate("invalid", "yyyy-MM-dd")).isNull();
}
@Test
@DisplayName("format: 패턴 적용")
void format() {
LocalDate d = LocalDate.of(2026, 5, 10);
assertThat(DateUtil.format(d, "yyyy-MM-dd")).isEqualTo("2026-05-10");
assertThat(DateUtil.format(d, "yyyyMMdd")).isEqualTo("20260510");
assertThat(DateUtil.format(null, "yyyy-MM-dd")).isNull();
}
@Test
@DisplayName("getMonthRange: 잘못된 yyyymm 은 예외")
void monthRange_invalid() {
assertThatThrownBy(() -> DateUtil.getMonthRange("2026"))
.isInstanceOf(java.time.format.DateTimeParseException.class);
}
@Test
@DisplayName("getSettleMonth(LocalDate): yyyyMM")
void settleMonthOfDate() {
assertThat(DateUtil.getSettleMonth(LocalDate.of(2026, 5, 10))).isEqualTo("202605");
assertThat(DateUtil.getSettleMonth(LocalDate.of(2024, 12, 31))).isEqualTo("202412");
}
}
@@ -0,0 +1,63 @@
package com.ga.common.util;
import com.ga.common.exception.BizException;
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;
class EncryptUtilTest {
private final EncryptUtil util = new EncryptUtil("ga-test-key-1234567890-abcdefghi");
@Test
@DisplayName("encrypt → decrypt round-trip")
void roundTrip() {
String plain = "9001011234567"; // 주민번호 형태
String enc = util.encrypt(plain);
assertThat(enc).isNotEqualTo(plain);
assertThat(enc).isBase64();
assertThat(util.decrypt(enc)).isEqualTo(plain);
}
@Test
@DisplayName("동일 평문 → 동일 암호문 (고정 IV)")
void deterministic() {
String enc1 = util.encrypt("hello");
String enc2 = util.encrypt("hello");
assertThat(enc1).isEqualTo(enc2);
}
@Test
@DisplayName("null/empty 는 그대로 반환")
void nullEmpty() {
assertThat(util.encrypt(null)).isNull();
assertThat(util.encrypt("")).isEmpty();
assertThat(util.decrypt(null)).isNull();
assertThat(util.decrypt("")).isEmpty();
}
@Test
@DisplayName("키가 32바이트 미만이면 0 패딩 → 정상 동작")
void shortKey() {
EncryptUtil shortKey = new EncryptUtil("short");
String enc = shortKey.encrypt("data");
assertThat(shortKey.decrypt(enc)).isEqualTo("data");
}
@Test
@DisplayName("잘못된 암호문 복호화 시 BizException")
void decryptInvalid() {
assertThatThrownBy(() -> util.decrypt("not-base64-!!"))
.isInstanceOf(BizException.class);
}
@Test
@DisplayName("한글/특수문자 round-trip")
void multiByte() {
String plain = "홍길동@한국#$% áéíóú";
assertThat(util.decrypt(util.encrypt(plain))).isEqualTo(plain);
}
}
@@ -0,0 +1,71 @@
package com.ga.common.util;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class MaskUtilTest {
@Test
@DisplayName("maskName: 길이별 분기")
void maskName() {
assertThat(MaskUtil.maskName("")).isEqualTo(""); // 1자: 그대로
assertThat(MaskUtil.maskName("홍길")).isEqualTo("홍*"); // 2자: 마지막
assertThat(MaskUtil.maskName("홍길동")).isEqualTo("홍*동"); // 3자: 가운데
assertThat(MaskUtil.maskName("홍길동이")).isEqualTo("홍**이"); // 4자
}
@Test
@DisplayName("maskPhone: 11자리 / 10자리")
void maskPhone() {
assertThat(MaskUtil.maskPhone("010-1234-5678")).isEqualTo("010-****-5678");
assertThat(MaskUtil.maskPhone("01012345678")).isEqualTo("010-****-5678");
assertThat(MaskUtil.maskPhone("031-123-4567")).isEqualTo("031-***-4567"); // 10자리
assertThat(MaskUtil.maskPhone("02-123-4567")).isEqualTo("02-123-4567"); // 9자리는 원본
}
@Test
@DisplayName("maskRrn: 13자리만 마스킹, 그 외 원본")
void maskRrn() {
assertThat(MaskUtil.maskRrn("880101-1234567")).isEqualTo("880101-*******");
assertThat(MaskUtil.maskRrn("8801011234567")).isEqualTo("880101-*******");
// 13자리가 아니면 원본 반환
assertThat(MaskUtil.maskRrn("88010")).isEqualTo("88010");
}
@Test
@DisplayName("maskAccount: 하이픈 포함 / 미포함")
void maskAccount() {
assertThat(MaskUtil.maskAccount("110-123-456789")).isEqualTo("110-***-******");
// 하이픈 없는 경우, 4자리 노출 후 마스킹
assertThat(MaskUtil.maskAccount("110123456789")).isEqualTo("1101********");
assertThat(MaskUtil.maskAccount("123")).isEqualTo("123"); // 4자 이하는 원본
}
@Test
@DisplayName("maskEmail: 첫 글자만 노출")
void maskEmail() {
assertThat(MaskUtil.maskEmail("aaa@bbb.com")).isEqualTo("a**@bbb.com");
assertThat(MaskUtil.maskEmail("longusername@domain.io")).isEqualTo("l***********@domain.io");
// @ 위치가 0 또는 1이면 마스킹 불가 → 원본
assertThat(MaskUtil.maskEmail("@bbb.com")).isEqualTo("@bbb.com");
assertThat(MaskUtil.maskEmail("a@bbb.com")).isEqualTo("a@bbb.com");
}
@Test
@DisplayName("mask: enum 디스패치")
void mask_enum() {
assertThat(MaskUtil.mask("홍길동", MaskUtil.MaskType.NAME)).isEqualTo("홍*동");
assertThat(MaskUtil.mask("010-1234-5678", MaskUtil.MaskType.PHONE)).isEqualTo("010-****-5678");
assertThat(MaskUtil.mask("aaa@bbb.com", MaskUtil.MaskType.EMAIL)).isEqualTo("a**@bbb.com");
}
@Test
@DisplayName("mask: null/blank 안전")
void mask_nullSafe() {
assertThat(MaskUtil.mask(null, MaskUtil.MaskType.NAME)).isNull();
assertThat(MaskUtil.mask("", MaskUtil.MaskType.NAME)).isEmpty();
assertThat(MaskUtil.mask(" ", MaskUtil.MaskType.NAME)).isEqualTo(" ");
}
}
@@ -0,0 +1,101 @@
package com.ga.common.util;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.assertj.core.api.Assertions.assertThat;
class MoneyUtilTest {
@Test
@DisplayName("zero: null 은 0 으로 치환")
void zero_null() {
assertThat(MoneyUtil.zero(null)).isEqualTo(BigDecimal.ZERO);
assertThat(MoneyUtil.zero(new BigDecimal("12.34"))).isEqualByComparingTo("12.34");
}
@Test
@DisplayName("add/sub: null 안전, 정상 합/차")
void add_sub() {
assertThat(MoneyUtil.add(null, new BigDecimal("100"))).isEqualByComparingTo("100");
assertThat(MoneyUtil.add(new BigDecimal("100"), null)).isEqualByComparingTo("100");
assertThat(MoneyUtil.add(new BigDecimal("100"), new BigDecimal("50.50"))).isEqualByComparingTo("150.50");
assertThat(MoneyUtil.sub(new BigDecimal("100"), new BigDecimal("30"))).isEqualByComparingTo("70");
assertThat(MoneyUtil.sub(null, null)).isEqualByComparingTo("0");
}
@Test
@DisplayName("mul: 소수점 2자리 HALF_UP")
void mul_halfUp() {
// 1.235 → 1.24 (HALF_UP)
assertThat(MoneyUtil.mul(new BigDecimal("0.5"), new BigDecimal("2.47")))
.isEqualByComparingTo("1.24");
assertThat(MoneyUtil.mul(null, new BigDecimal("100"))).isEqualByComparingTo("0.00");
}
@Test
@DisplayName("div: 0 으로 나누면 0 반환, null 안전")
void div_byZero() {
assertThat(MoneyUtil.div(new BigDecimal("100"), BigDecimal.ZERO)).isEqualByComparingTo("0");
assertThat(MoneyUtil.div(new BigDecimal("100"), null)).isEqualByComparingTo("0");
assertThat(MoneyUtil.div(new BigDecimal("100"), new BigDecimal("3")))
.isEqualByComparingTo("33.33");
}
@Test
@DisplayName("pct: amount × rate / 100, 2자리 HALF_UP")
void pct_basic() {
// 1,000,000 × 7.5% = 75,000.00
assertThat(MoneyUtil.pct(new BigDecimal("1000000"), new BigDecimal("7.5")))
.isEqualByComparingTo("75000.00");
// 100 × 33.33% = 33.33
assertThat(MoneyUtil.pct(new BigDecimal("100"), new BigDecimal("33.33")))
.isEqualByComparingTo("33.33");
}
@Test
@DisplayName("pct: null 입력은 0")
void pct_null() {
assertThat(MoneyUtil.pct(null, new BigDecimal("10"))).isEqualByComparingTo("0");
assertThat(MoneyUtil.pct(new BigDecimal("100"), null)).isEqualByComparingTo("0");
}
@Test
@DisplayName("tax: 기본 3.3% 원천징수")
void tax_default() {
// 1,000,000 × 3.3% = 33,000.00
assertThat(MoneyUtil.tax(new BigDecimal("1000000")))
.isEqualByComparingTo("33000.00");
}
@Test
@DisplayName("tax: 사용자 지정 세율")
void tax_customRate() {
assertThat(MoneyUtil.tax(new BigDecimal("1000"), new BigDecimal("10")))
.isEqualByComparingTo("100.00");
}
@Test
@DisplayName("isZero/isPositive/isNegative")
void signChecks() {
assertThat(MoneyUtil.isZero(null)).isTrue();
assertThat(MoneyUtil.isZero(BigDecimal.ZERO)).isTrue();
assertThat(MoneyUtil.isZero(new BigDecimal("0.00"))).isTrue();
assertThat(MoneyUtil.isPositive(new BigDecimal("0.01"))).isTrue();
assertThat(MoneyUtil.isPositive(null)).isFalse();
assertThat(MoneyUtil.isNegative(new BigDecimal("-1"))).isTrue();
assertThat(MoneyUtil.isNegative(BigDecimal.ZERO)).isFalse();
}
@Test
@DisplayName("fmt/fmtDec/fmtSign")
void formatting() {
assertThat(MoneyUtil.fmt(new BigDecimal("1234567"))).isEqualTo("1,234,567");
assertThat(MoneyUtil.fmt(null)).isEmpty();
assertThat(MoneyUtil.fmtDec(new BigDecimal("1234.5"))).isEqualTo("1,234.50");
assertThat(MoneyUtil.fmtSign(new BigDecimal("100"))).isEqualTo("+100");
assertThat(MoneyUtil.fmtSign(new BigDecimal("-100"))).isEqualTo("-100");
}
}