diff --git a/card-settlement-migration/src/test/java/com/bais/card/settlement/SettlementCalculatorTest.java b/card-settlement-migration/src/test/java/com/bais/card/settlement/SettlementCalculatorTest.java new file mode 100644 index 0000000..75b58a6 --- /dev/null +++ b/card-settlement-migration/src/test/java/com/bais/card/settlement/SettlementCalculatorTest.java @@ -0,0 +1,193 @@ +package com.bais.card.settlement; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import java.math.BigDecimal; +import static org.junit.jupiter.api.Assertions.*; + +/** + * SettlementCalculator 단위 테스트 + * 불변식 검증: BRAND_SETTLEMENT_*, IRD_*, ISSUER_FEE_*, AMOUNT_*, ROUNDING_MODE + */ +@DisplayName("SettlementCalculator 불변식 테스트") +class SettlementCalculatorTest { + + private SettlementCalculator calculator; + + @BeforeEach + void setUp() { + calculator = new SettlementCalculator(); + } + + @Nested + @DisplayName("브랜드 정산 불변식 테스트") + class BrandSettlementTests { + + @Test + @DisplayName("BRAND_SETTLEMENT_FORMULA: 정산금 = 승인금액 - 취소금액 - 수수료") + void testSettlementFormula() { + BigDecimal approved = new BigDecimal("100000"); + BigDecimal cancelled = new BigDecimal("10000"); + BigDecimal rate = new BigDecimal("0.02"); + + SettlementCalculator.BrandSettlementResult result = + calculator.calculateBrandSettlement(approved, cancelled, rate); + + assertEquals(new BigDecimal("2000.00"), result.getCommission()); + assertEquals(new BigDecimal("88000.00"), result.getSettlementAmount()); + } + + @Test + @DisplayName("BRAND_SETTLEMENT_NON_NEGATIVE: 정산금은 0 이상") + void testSettlementNonNegative() { + SettlementCalculator.BrandSettlementResult result = + calculator.calculateBrandSettlement( + new BigDecimal("10000"), new BigDecimal("20000"), new BigDecimal("0.02")); + assertEquals(BigDecimal.ZERO.setScale(2), result.getSettlementAmount()); + } + + @Test + @DisplayName("BRAND_COMMISSION_RATE_DEFAULT: 미지정 시 0.02") + void testDefaultCommissionRate() { + SettlementCalculator.BrandSettlementResult result = + calculator.calculateBrandSettlement( + new BigDecimal("100000"), BigDecimal.ZERO, null); + assertEquals(new BigDecimal("0.02"), result.getCommissionRate()); + } + } + + @Nested + @DisplayName("IRD 불변식 테스트") + class IrdTests { + + @Test + @DisplayName("IRD_FORMULA: IRD = 승인금액 * IRD율") + void testIrdFormula() { + BigDecimal ird = calculator.calculateIRD( + new BigDecimal("100000"), new BigDecimal("0.015")); + assertEquals(new BigDecimal("1500.00"), ird); + } + + @Test + @DisplayName("IRD_RATE_DEFAULT: 미지정 시 0.015") + void testDefaultIrdRate() { + BigDecimal ird = calculator.calculateIRD(new BigDecimal("100000"), null); + assertEquals(new BigDecimal("1500.00"), ird); + } + + @Test + @DisplayName("IRD_NON_NEGATIVE: IRD는 0 이상") + void testIrdNonNegative() { + BigDecimal ird = calculator.calculateIRD( + new BigDecimal("100000"), new BigDecimal("-0.015")); + assertTrue(ird.compareTo(BigDecimal.ZERO) >= 0); + } + } + + @Nested + @DisplayName("발급사 수수료 불변식 테스트") + class IssuerFeeTests { + + @Test + @DisplayName("ISSUER_FEE_FORMULA: 발급사 수수료 = IRD * 발급사 수수료율") + void testIssuerFeeFormula() { + BigDecimal fee = calculator.calculateIssuerFee( + new BigDecimal("1500.00"), new BigDecimal("0.005")); + assertEquals(new BigDecimal("7.50"), fee); + } + + @Test + @DisplayName("ISSUER_FEE_RATE_DEFAULT: 미지정 시 0.005") + void testDefaultIssuerFeeRate() { + BigDecimal fee = calculator.calculateIssuerFee(new BigDecimal("1500.00"), null); + assertEquals(new BigDecimal("7.50"), fee); + } + + @Test + @DisplayName("ISSUER_SETTLEMENT_FORMULA: 발급사 정산금 = IRD - 발급사 수수료") + void testIssuerSettlementFormula() { + SettlementCalculator.IssuerSettlementResult result = + calculator.calculateIssuerSettlement( + new BigDecimal("1500.00"), new BigDecimal("0.005")); + assertEquals(new BigDecimal("7.50"), result.getIssuerFee()); + assertEquals(new BigDecimal("1492.50"), result.getIssuerSettlement()); + } + } + + @Nested + @DisplayName("공통 금액 검증 불변식 테스트") + class AmountValidationTests { + + @Test + @DisplayName("AMOUNT_NOT_NULL: null 시 IllegalArgumentException") + void testNullAmount() { + assertThrows(IllegalArgumentException.class, () -> + calculator.calculateBrandSettlement(null, BigDecimal.ZERO, new BigDecimal("0.02"))); + } + + @Test + @DisplayName("AMOUNT_NON_NEGATIVE: 음수 시 IllegalArgumentException") + void testNegativeAmount() { + assertThrows(IllegalArgumentException.class, () -> + calculator.calculateBrandSettlement( + new BigDecimal("-100000"), BigDecimal.ZERO, new BigDecimal("0.02"))); + } + + @Test + @DisplayName("AMOUNT_MAX_LIMIT: 최대 한도 초과 시 IllegalArgumentException") + void testAmountExceedsMaxLimit() { + BigDecimal exceedsMax = new BigDecimal("999999999999.99").add(BigDecimal.ONE); + assertThrows(IllegalArgumentException.class, () -> + calculator.calculateBrandSettlement( + exceedsMax, BigDecimal.ZERO, new BigDecimal("0.02"))); + } + } + + @Nested + @DisplayName("금액 정밀도 불변식 테스트") + class PrecisionTests { + + @Test + @DisplayName("AMOUNT_SCALE: 소수점 2자리") + void testAmountScale() { + SettlementCalculator.BrandSettlementResult result = + calculator.calculateBrandSettlement( + new BigDecimal("100000"), BigDecimal.ZERO, new BigDecimal("0.023456")); + assertEquals(2, result.getCommission().scale()); + assertEquals(new BigDecimal("2345.60"), result.getCommission()); + } + + @Test + @DisplayName("ROUNDING_MODE: HALF_UP 반올림") + void testHalfUpRounding() { + SettlementCalculator.BrandSettlementResult result = + calculator.calculateBrandSettlement( + new BigDecimal("100000"), BigDecimal.ZERO, new BigDecimal("0.023455")); + assertEquals(new BigDecimal("2345.50"), result.getCommission()); + } + } + + @Nested + @DisplayName("전체 정산 흐름 테스트") + class FullSettlementTests { + + @Test + @DisplayName("전체 정산 계산 통합 테스트") + void testFullSettlement() { + SettlementCalculator.FullSettlementResult result = + calculator.calculateFullSettlement( + new BigDecimal("100000"), + new BigDecimal("10000"), + new BigDecimal("0.02"), + new BigDecimal("0.015"), + new BigDecimal("0.005")); + + assertNotNull(result.getBrandSettlement()); + assertNotNull(result.getIrd()); + assertNotNull(result.getIssuerSettlement()); + assertEquals(new BigDecimal("1500.00"), result.getIrd()); + } + } +}