Compare commits
9 commits
main
...
forge/code
| Author | SHA1 | Date | |
|---|---|---|---|
| a3f8391554 | |||
| 7f77947983 | |||
| b50dd867f3 | |||
| b83a09660f | |||
| 68a4df5aaf | |||
| 8513e6b71c | |||
| b7784afef8 | |||
| 315eaf3823 | |||
| 378c496f78 |
9 changed files with 866 additions and 0 deletions
|
|
@ -0,0 +1,3 @@
|
|||
# codex-bais-final3-20260713-192718-BAIS-SPRING-ANA-CARD-001-attempt-1-run-4a55ce2ead99
|
||||
|
||||
Forge 이슈 작업 브랜치 `forge/codex-bais-final3-20260713-192718-BAIS-SPRING-ANA-CARD-001-attempt-1-run-4a55ce2ead99`.
|
||||
94
card-settlement-migration/INVARIANTS.md
Normal file
94
card-settlement-migration/INVARIANTS.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# 카드 정산 불변식 표 (Card Settlement Invariants)
|
||||
|
||||
**범위**: card-settlement-migration
|
||||
|
||||
## 1. 브랜드 정산 (Brand Settlement) 불변식
|
||||
|
||||
| ID | 설명 | 수식 | 영향 클래스 | 보존 주의사항 |
|
||||
|---|---|---|---|---|
|
||||
| BRAND_SETTLEMENT_NON_NEGATIVE | 브랜드 정산금은 0 이상 | settlementAmount >= 0 | SettlementCalculator | 음수 시 0 보정 |
|
||||
| BRAND_SETTLEMENT_FORMULA | 정산금 = 승인금액 - 취소금액 - 수수료 | settlementAmount = approved - cancelled - commission | SettlementCalculator | 계산 순서 준수 |
|
||||
| BRAND_COMMISSION_FORMULA | 수수료 = 승인금액 * 정산요율 | commission = approvedAmount * rate | SettlementCalculator | 2자리 반올림 |
|
||||
| BRAND_COMMISSION_RATE_DEFAULT | 미지정 시 기본값 0.02(2%) | rate = 0.02 when null | SettlementCalculator | DEFAULT_COMMISSION_RATE |
|
||||
|
||||
## 2. IRD (Interchange Reimbursement Fee) 불변식
|
||||
|
||||
| ID | 설명 | 수식 | 영향 클래스 | 보존 주의사항 |
|
||||
|---|---|---|---|---|
|
||||
| IRD_NON_NEGATIVE | IRD는 0 이상 | ird >= 0 | SettlementCalculator | 음수 시 0 보정 |
|
||||
| IRD_FORMULA | IRD = 승인금액 * IRD율 | ird = approvedAmount * rate | SettlementCalculator | 2자리 반올림 |
|
||||
| IRD_RATE_DEFAULT | 미지정 시 기본값 0.015(1.5%) | rate = 0.015 when null | SettlementCalculator | DEFAULT_IRD_RATE |
|
||||
|
||||
## 3. 발급사 수수료 (Issuer Fee) 불변식
|
||||
|
||||
| ID | 설명 | 수식 | 영향 클래스 | 보존 주의사항 |
|
||||
|---|---|---|---|---|
|
||||
| ISSUER_FEE_NON_NEGATIVE | 발급사 수수료는 0 이상 | issuerFee >= 0 | SettlementCalculator | 음수 시 0 보정 |
|
||||
| ISSUER_FEE_FORMULA | 발급사 수수료 = IRD * 발급사 수수료율 | issuerFee = ird * rate | SettlementCalculator | 2자리 반올림 |
|
||||
| ISSUER_FEE_RATE_DEFAULT | 미지정 시 기본값 0.005(0.5%) | rate = 0.005 when null | SettlementCalculator | DEFAULT_ISSUER_FEE_RATE |
|
||||
| ISSUER_SETTLEMENT_NON_NEGATIVE | 발급사 정산금은 0 이상 | issuerSettlement >= 0 | SettlementCalculator | 음수 시 0 보정 |
|
||||
| ISSUER_SETTLEMENT_FORMULA | 발급사 정산금 = IRD - 발급사 수수료 | issuerSettlement = ird - fee | SettlementCalculator | 순서 준수 |
|
||||
|
||||
## 4. 공통 금액 검증 불변식
|
||||
|
||||
| ID | 설명 | 수식 | 영향 클래스 | 보존 주의사항 |
|
||||
|---|---|---|---|---|
|
||||
| AMOUNT_NOT_NULL | 금액은 null 불가 | amount != null | SettlementCalculator | IllegalArgumentException |
|
||||
| AMOUNT_NON_NEGATIVE | 금액은 0 이상 | amount >= 0 | SettlementCalculator | IllegalArgumentException |
|
||||
| AMOUNT_MAX_LIMIT | 최대 한도 이하여야 함 | amount <= 999999999999.99 | SettlementCalculator | IllegalArgumentException |
|
||||
|
||||
## 5. 금액 정밀도 불변식
|
||||
|
||||
| ID | 설명 | 수식 | 영향 클래스 | 보존 주의사항 |
|
||||
|---|---|---|---|---|
|
||||
| AMOUNT_SCALE | 소수점 2자리 표현 | scale(amount) = 2 | SettlementCalculator | setScale(2, HALF_UP) |
|
||||
| ROUNDING_MODE | HALF_UP 반올림 | RoundingMode.HALF_UP | SettlementCalculator | 반올림 정책 준수 |
|
||||
|
||||
## 6. 계산 흐름도
|
||||
|
||||
```
|
||||
승인요청
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ SettlementCalculator │
|
||||
│ ───────────────────────── │
|
||||
│ [브랜드 정산 계산] │
|
||||
│ 1. 승인금액 검증 (NOT_NULL, │
|
||||
│ NON_NEGATIVE, MAX_LIMIT)│
|
||||
│ 2. 수수료 = 승인금액 * 요율 │
|
||||
│ 3. IRD = 승인금액 * IRD율 │
|
||||
│ 4. 정산금 = 승인 - 취소 - 수수료│
|
||||
│ 5. 음수 보정 (>= 0) │
|
||||
│ ───────────────────────── │
|
||||
│ [발급사 수수료 계산] │
|
||||
│ 6. 발급사수수료 = IRD * 요율│
|
||||
│ 7. 발급사정산 = IRD - 수수료│
|
||||
│ 8. 음수 보정 (>= 0) │
|
||||
└─────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
정산 결과 출력
|
||||
```
|
||||
|
||||
## 7. Spring 전환 시 체크리스트
|
||||
|
||||
- [ ] SettlementCalculator Bean 등록 및 의존성 주입 확인
|
||||
- [ ] 정산요율 기본값 (@Value 또는 @ConfigurationProperties) 설정
|
||||
- [ ] IRD율 기본값 설정
|
||||
- [ ] 발급사 수수료율 기본값 설정
|
||||
- [ ] 금액 검증 (@Valid, @DecimalMin, @DecimalMax) 어노테이션 적용
|
||||
- [ ] 반올림 정책 (HALF_UP) Bean 또는 Util로 분리
|
||||
- [ ] 단위 테스트: 각 불변식별 테스트 케이스 구현
|
||||
- [ ] 통합 테스트: 계산 흐름 전체 검증
|
||||
|
||||
## 8. 불변식 요약
|
||||
|
||||
| 카테고리 | 불변식 수 |
|
||||
|---|---|
|
||||
| 브랜드 정산 | 4 |
|
||||
| IRD | 3 |
|
||||
| 발급사 수수료 | 5 |
|
||||
| 공통 금액 검증 | 3 |
|
||||
| 금액 정밀도 | 2 |
|
||||
| **총계** | **17** |
|
||||
41
card-settlement-migration/README.md
Normal file
41
card-settlement-migration/README.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Card Settlement Migration Module
|
||||
|
||||
카드 정산 마이그레이션 모듈
|
||||
|
||||
## 개요
|
||||
|
||||
card-settlement-migration은 카드 정산 업무 규칙을 Spring 전환 시 보존하기 위한 불변식 정의 및 계산 로직을 포함합니다.
|
||||
|
||||
## 주요 기능
|
||||
|
||||
- **브랜드 정산 계산**: 가맹점 정산금 = 승인금액 - 취소금액 - 수수료
|
||||
- **IRD 계산**: Interchange Reimbursement Fee = 승인금액 * IRD율
|
||||
- **발급사 수수료 계산**: 발급사 수수료 = IRD * 발급사 수수료율
|
||||
|
||||
## 불변식 (Invariants)
|
||||
|
||||
총 17개의 불변식이 정의되어 있습니다. 자세한 내용은 `INVARIANTS.md`를 참조하세요.
|
||||
|
||||
### 카테고리별 불변식
|
||||
|
||||
| 카테고리 | 불변식 수 |
|
||||
|---|---|
|
||||
| 브랜드 정산 | 4 |
|
||||
| IRD | 3 |
|
||||
| 발급사 수수료 | 5 |
|
||||
| 공통 금액 검증 | 3 |
|
||||
| 금액 정밀도 | 2 |
|
||||
|
||||
## 빌드 및 테스트
|
||||
|
||||
```bash
|
||||
mvn clean test
|
||||
```
|
||||
|
||||
## Spring 전환 체크리스트
|
||||
|
||||
- [ ] SettlementCalculator Bean 등록
|
||||
- [ ] 정산요율, IRD율, 발급사 수수료율 기본값 설정
|
||||
- [ ] 금액 검증 어노테이션 적용
|
||||
- [ ] 반올림 정책 Bean 또는 Util 분리
|
||||
- [ ] 단위 테스트 및 통합 테스트 실행
|
||||
50
card-settlement-migration/pom.xml
Normal file
50
card-settlement-migration/pom.xml
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.bais</groupId>
|
||||
<artifactId>codex-bais-final3-20260713-192718</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>card-settlement-migration</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Card Settlement Migration</name>
|
||||
<description>카드 정산 마이그레이션 모듈 - 브랜드 정산, IRD, 발급사 수수료 계산</description>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<junit.version>5.10.2</junit.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.2.5</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
package com.bais.card.settlement;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Settlement Calculator
|
||||
*
|
||||
* 카드 정산 계산 로직 (Brand Settlement + IRD + Issuer Fee)
|
||||
*
|
||||
* INVARIANTS:
|
||||
* - BRAND_SETTLEMENT_NON_NEGATIVE: 정산금은 0 이상
|
||||
* - BRAND_SETTLEMENT_FORMULA: 정산금 = 승인금액 - 취소금액 - 수수료
|
||||
* - BRAND_COMMISSION_FORMULA: 수수료 = 승인금액 * 정산요율
|
||||
* - BRAND_COMMISSION_RATE_DEFAULT: 미지정 시 0.02
|
||||
* - IRD_NON_NEGATIVE: IRD는 0 이상
|
||||
* - IRD_FORMULA: IRD = 승인금액 * IRD율
|
||||
* - IRD_RATE_DEFAULT: 미지정 시 0.015
|
||||
* - ISSUER_FEE_NON_NEGATIVE: 발급사 수수료는 0 이상
|
||||
* - ISSUER_FEE_FORMULA: 발급사 수수료 = IRD * 발급사 수수료율
|
||||
* - ISSUER_FEE_RATE_DEFAULT: 미지정 시 0.005
|
||||
* - ISSUER_SETTLEMENT_NON_NEGATIVE: 발급사 정산금은 0 이상
|
||||
* - ISSUER_SETTLEMENT_FORMULA: 발급사 정산금 = IRD - 발급사 수수료
|
||||
* - AMOUNT_NOT_NULL: 금액은 null 불가
|
||||
* - AMOUNT_NON_NEGATIVE: 금액은 0 이상
|
||||
* - AMOUNT_MAX_LIMIT: 최대 한도 이하여야 함
|
||||
* - AMOUNT_SCALE: 소수점 2자리 표현
|
||||
* - ROUNDING_MODE: HALF_UP 반올림
|
||||
*/
|
||||
public class SettlementCalculator {
|
||||
|
||||
private static final BigDecimal DEFAULT_COMMISSION_RATE = new BigDecimal("0.02");
|
||||
private static final BigDecimal DEFAULT_IRD_RATE = new BigDecimal("0.015");
|
||||
private static final BigDecimal DEFAULT_ISSUER_FEE_RATE = new BigDecimal("0.005");
|
||||
private static final BigDecimal MIN_AMOUNT = BigDecimal.ZERO;
|
||||
private static final BigDecimal MAX_AMOUNT = new BigDecimal("999999999999.99");
|
||||
|
||||
/**
|
||||
* 브랜드 정산금 계산
|
||||
* INVARIANT: 정산금 = 승인금액 - 취소금액 - 수수료
|
||||
*/
|
||||
public BrandSettlementResult calculateBrandSettlement(BigDecimal approvedAmount,
|
||||
BigDecimal cancelledAmount,
|
||||
BigDecimal commissionRate) {
|
||||
validateAmount(approvedAmount);
|
||||
validateAmount(cancelledAmount);
|
||||
|
||||
BigDecimal effectiveCommissionRate =
|
||||
commissionRate != null ? commissionRate : DEFAULT_COMMISSION_RATE;
|
||||
|
||||
BigDecimal commission = approvedAmount
|
||||
.multiply(effectiveCommissionRate)
|
||||
.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
|
||||
BigDecimal settlementAmount = approvedAmount
|
||||
.subtract(cancelledAmount)
|
||||
.subtract(commission);
|
||||
|
||||
if (settlementAmount.compareTo(MIN_AMOUNT) < 0) {
|
||||
settlementAmount = MIN_AMOUNT;
|
||||
}
|
||||
|
||||
return new BrandSettlementResult(
|
||||
settlementAmount, commission, approvedAmount, cancelledAmount,
|
||||
effectiveCommissionRate, LocalDateTime.now()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* IRD 계산
|
||||
* INVARIANT: IRD = 승인금액 * IRD율
|
||||
*/
|
||||
public BigDecimal calculateIRD(BigDecimal approvedAmount, BigDecimal irdRate) {
|
||||
validateAmount(approvedAmount);
|
||||
|
||||
BigDecimal effectiveIrdRate = irdRate != null ? irdRate : DEFAULT_IRD_RATE;
|
||||
|
||||
BigDecimal ird = approvedAmount
|
||||
.multiply(effectiveIrdRate)
|
||||
.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
|
||||
if (ird.compareTo(MIN_AMOUNT) < 0) {
|
||||
return MIN_AMOUNT;
|
||||
}
|
||||
|
||||
return ird;
|
||||
}
|
||||
|
||||
/**
|
||||
* 발급사 수수료 계산
|
||||
* INVARIANT: 발급사 수수료 = IRD * 발급사 수수료율
|
||||
*/
|
||||
public BigDecimal calculateIssuerFee(BigDecimal ird, BigDecimal issuerFeeRate) {
|
||||
validateAmount(ird);
|
||||
|
||||
BigDecimal effectiveRate =
|
||||
issuerFeeRate != null ? issuerFeeRate : DEFAULT_ISSUER_FEE_RATE;
|
||||
|
||||
BigDecimal issuerFee = ird
|
||||
.multiply(effectiveRate)
|
||||
.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
|
||||
if (issuerFee.compareTo(MIN_AMOUNT) < 0) {
|
||||
return MIN_AMOUNT;
|
||||
}
|
||||
|
||||
return issuerFee;
|
||||
}
|
||||
|
||||
/**
|
||||
* 발급사 정산금 계산
|
||||
* INVARIANT: 발급사 정산금 = IRD - 발급사 수수료
|
||||
*/
|
||||
public IssuerSettlementResult calculateIssuerSettlement(BigDecimal ird,
|
||||
BigDecimal issuerFeeRate) {
|
||||
validateAmount(ird);
|
||||
|
||||
BigDecimal effectiveRate =
|
||||
issuerFeeRate != null ? issuerFeeRate : DEFAULT_ISSUER_FEE_RATE;
|
||||
|
||||
BigDecimal issuerFee = ird
|
||||
.multiply(effectiveRate)
|
||||
.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
|
||||
BigDecimal issuerSettlement = ird.subtract(issuerFee);
|
||||
|
||||
if (issuerSettlement.compareTo(MIN_AMOUNT) < 0) {
|
||||
issuerSettlement = MIN_AMOUNT;
|
||||
}
|
||||
|
||||
return new IssuerSettlementResult(
|
||||
issuerSettlement, issuerFee, ird, effectiveRate, LocalDateTime.now()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 전체 정산 계산 (Brand + Issuer)
|
||||
*/
|
||||
public FullSettlementResult calculateFullSettlement(BigDecimal approvedAmount,
|
||||
BigDecimal cancelledAmount,
|
||||
BigDecimal commissionRate,
|
||||
BigDecimal irdRate,
|
||||
BigDecimal issuerFeeRate) {
|
||||
BrandSettlementResult brandResult = calculateBrandSettlement(
|
||||
approvedAmount, cancelledAmount, commissionRate);
|
||||
|
||||
BigDecimal ird = calculateIRD(approvedAmount, irdRate);
|
||||
IssuerSettlementResult issuerResult = calculateIssuerSettlement(ird, issuerFeeRate);
|
||||
|
||||
return new FullSettlementResult(
|
||||
brandResult.getSettlementAmount(),
|
||||
brandResult.getCommission(),
|
||||
ird,
|
||||
issuerResult.getIssuerFee(),
|
||||
issuerResult.getIssuerSettlement(),
|
||||
LocalDateTime.now()
|
||||
);
|
||||
}
|
||||
|
||||
private void validateAmount(BigDecimal amount) {
|
||||
Objects.requireNonNull(amount, "Amount must not be null");
|
||||
if (amount.compareTo(MIN_AMOUNT) < 0) {
|
||||
throw new IllegalArgumentException("Amount must be non-negative");
|
||||
}
|
||||
if (amount.compareTo(MAX_AMOUNT) > 0) {
|
||||
throw new IllegalArgumentException("Amount exceeds maximum limit");
|
||||
}
|
||||
}
|
||||
|
||||
public static class BrandSettlementResult {
|
||||
private final BigDecimal settlementAmount;
|
||||
private final BigDecimal commission;
|
||||
private final BigDecimal approvedAmount;
|
||||
private final BigDecimal cancelledAmount;
|
||||
private final BigDecimal commissionRate;
|
||||
private final LocalDateTime calculatedAt;
|
||||
|
||||
public BrandSettlementResult(BigDecimal settlementAmount, BigDecimal commission,
|
||||
BigDecimal approvedAmount, BigDecimal cancelledAmount,
|
||||
BigDecimal commissionRate, LocalDateTime calculatedAt) {
|
||||
this.settlementAmount = settlementAmount;
|
||||
this.commission = commission;
|
||||
this.approvedAmount = approvedAmount;
|
||||
this.cancelledAmount = cancelledAmount;
|
||||
this.commissionRate = commissionRate;
|
||||
this.calculatedAt = calculatedAt;
|
||||
}
|
||||
|
||||
public BigDecimal getSettlementAmount() { return settlementAmount; }
|
||||
public BigDecimal getCommission() { return commission; }
|
||||
public BigDecimal getApprovedAmount() { return approvedAmount; }
|
||||
public BigDecimal getCancelledAmount() { return cancelledAmount; }
|
||||
public BigDecimal getCommissionRate() { return commissionRate; }
|
||||
public LocalDateTime getCalculatedAt() { return calculatedAt; }
|
||||
}
|
||||
|
||||
public static class IssuerSettlementResult {
|
||||
private final BigDecimal issuerSettlement;
|
||||
private final BigDecimal issuerFee;
|
||||
private final BigDecimal ird;
|
||||
private final BigDecimal issuerFeeRate;
|
||||
private final LocalDateTime calculatedAt;
|
||||
|
||||
public IssuerSettlementResult(BigDecimal issuerSettlement, BigDecimal issuerFee,
|
||||
BigDecimal ird, BigDecimal issuerFeeRate,
|
||||
LocalDateTime calculatedAt) {
|
||||
this.issuerSettlement = issuerSettlement;
|
||||
this.issuerFee = issuerFee;
|
||||
this.ird = ird;
|
||||
this.issuerFeeRate = issuerFeeRate;
|
||||
this.calculatedAt = calculatedAt;
|
||||
}
|
||||
|
||||
public BigDecimal getIssuerSettlement() { return issuerSettlement; }
|
||||
public BigDecimal getIssuerFee() { return issuerFee; }
|
||||
public BigDecimal getIrd() { return ird; }
|
||||
public BigDecimal getIssuerFeeRate() { return issuerFeeRate; }
|
||||
public LocalDateTime getCalculatedAt() { return calculatedAt; }
|
||||
}
|
||||
|
||||
public static class FullSettlementResult {
|
||||
private final BigDecimal brandSettlement;
|
||||
private final BigDecimal brandCommission;
|
||||
private final BigDecimal ird;
|
||||
private final BigDecimal issuerFee;
|
||||
private final BigDecimal issuerSettlement;
|
||||
private final LocalDateTime calculatedAt;
|
||||
|
||||
public FullSettlementResult(BigDecimal brandSettlement, BigDecimal brandCommission,
|
||||
BigDecimal ird, BigDecimal issuerFee,
|
||||
BigDecimal issuerSettlement, LocalDateTime calculatedAt) {
|
||||
this.brandSettlement = brandSettlement;
|
||||
this.brandCommission = brandCommission;
|
||||
this.ird = ird;
|
||||
this.issuerFee = issuerFee;
|
||||
this.issuerSettlement = issuerSettlement;
|
||||
this.calculatedAt = calculatedAt;
|
||||
}
|
||||
|
||||
public BigDecimal getBrandSettlement() { return brandSettlement; }
|
||||
public BigDecimal getBrandCommission() { return brandCommission; }
|
||||
public BigDecimal getIrd() { return ird; }
|
||||
public BigDecimal getIssuerFee() { return issuerFee; }
|
||||
public BigDecimal getIssuerSettlement() { return issuerSettlement; }
|
||||
public LocalDateTime getCalculatedAt() { return calculatedAt; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package com.bais.card.settlement;
|
||||
|
||||
/**
|
||||
* Settlement Invariants Specification
|
||||
*
|
||||
* 카드 정산 업무 규칙과 Spring 전환 시 보존해야 할 불변식 표
|
||||
* 범위: card-settlement-migration
|
||||
*/
|
||||
public class SettlementInvariant {
|
||||
|
||||
/** 1. 브랜드 정산 불변식 */
|
||||
public static final Invariant BRAND_SETTLEMENT_NON_NEGATIVE =
|
||||
new Invariant("BRAND_SETTLEMENT_NON_NEGATIVE", "브랜드 정산금은 0 이상", "settlementAmount >= 0", "SettlementCalculator", "음수 시 0 보정");
|
||||
public static final Invariant BRAND_SETTLEMENT_FORMULA =
|
||||
new Invariant("BRAND_SETTLEMENT_FORMULA", "정산금 = 승인금액 - 취소금액 - 수수료", "settlementAmount = approved - cancelled - commission", "SettlementCalculator", "계산 순서 준수");
|
||||
public static final Invariant BRAND_COMMISSION_FORMULA =
|
||||
new Invariant("BRAND_COMMISSION_FORMULA", "수수료 = 승인금액 * 정산요율", "commission = approvedAmount * rate", "SettlementCalculator", "2자리 반올림");
|
||||
public static final Invariant BRAND_COMMISSION_RATE_DEFAULT =
|
||||
new Invariant("BRAND_COMMISSION_RATE_DEFAULT", "미지정 시 기본값 0.02(2%)", "rate = 0.02 when null", "SettlementCalculator", "DEFAULT_COMMISSION_RATE");
|
||||
|
||||
/** 2. IRD 불변식 */
|
||||
public static final Invariant IRD_NON_NEGATIVE =
|
||||
new Invariant("IRD_NON_NEGATIVE", "IRD는 0 이상", "ird >= 0", "SettlementCalculator", "음수 시 0 보정");
|
||||
public static final Invariant IRD_FORMULA =
|
||||
new Invariant("IRD_FORMULA", "IRD = 승인금액 * IRD율", "ird = approvedAmount * rate", "SettlementCalculator", "2자리 반올림");
|
||||
public static final Invariant IRD_RATE_DEFAULT =
|
||||
new Invariant("IRD_RATE_DEFAULT", "미지정 시 기본값 0.015(1.5%)", "rate = 0.015 when null", "SettlementCalculator", "DEFAULT_IRD_RATE");
|
||||
|
||||
/** 3. 발급사 수수료 불변식 */
|
||||
public static final Invariant ISSUER_FEE_NON_NEGATIVE =
|
||||
new Invariant("ISSUER_FEE_NON_NEGATIVE", "발급사 수수료는 0 이상", "issuerFee >= 0", "SettlementCalculator", "음수 시 0 보정");
|
||||
public static final Invariant ISSUER_FEE_FORMULA =
|
||||
new Invariant("ISSUER_FEE_FORMULA", "발급사 수수료 = IRD * 발급사 수수료율", "issuerFee = ird * rate", "SettlementCalculator", "2자리 반올림");
|
||||
public static final Invariant ISSUER_FEE_RATE_DEFAULT =
|
||||
new Invariant("ISSUER_FEE_RATE_DEFAULT", "미지정 시 기본값 0.005(0.5%)", "rate = 0.005 when null", "SettlementCalculator", "DEFAULT_ISSUER_FEE_RATE");
|
||||
public static final Invariant ISSUER_SETTLEMENT_NON_NEGATIVE =
|
||||
new Invariant("ISSUER_SETTLEMENT_NON_NEGATIVE", "발급사 정산금은 0 이상", "issuerSettlement >= 0", "SettlementCalculator", "음수 시 0 보정");
|
||||
public static final Invariant ISSUER_SETTLEMENT_FORMULA =
|
||||
new Invariant("ISSUER_SETTLEMENT_FORMULA", "발급사 정산금 = IRD - 발급사 수수료", "issuerSettlement = ird - fee", "SettlementCalculator", "순서 준수");
|
||||
|
||||
/** 4. 공통 금액 검증 불변식 */
|
||||
public static final Invariant AMOUNT_NOT_NULL =
|
||||
new Invariant("AMOUNT_NOT_NULL", "금액은 null 불가", "amount != null", "SettlementCalculator", "IllegalArgumentException");
|
||||
public static final Invariant AMOUNT_NON_NEGATIVE =
|
||||
new Invariant("AMOUNT_NON_NEGATIVE", "금액은 0 이상", "amount >= 0", "SettlementCalculator", "IllegalArgumentException");
|
||||
public static final Invariant AMOUNT_MAX_LIMIT =
|
||||
new Invariant("AMOUNT_MAX_LIMIT", "최대 한도 이하여야 함", "amount <= 999999999999.99", "SettlementCalculator", "IllegalArgumentException");
|
||||
|
||||
/** 5. 금액 정밀도 불변식 */
|
||||
public static final Invariant AMOUNT_SCALE =
|
||||
new Invariant("AMOUNT_SCALE", "소수점 2자리 표현", "scale(amount) = 2", "SettlementCalculator", "setScale(2, HALF_UP)");
|
||||
public static final Invariant ROUNDING_MODE =
|
||||
new Invariant("ROUNDING_MODE", "HALF_UP 반올림", "RoundingMode.HALF_UP", "SettlementCalculator", "반올림 정책 준수");
|
||||
|
||||
public static Invariant[] getAllInvariants() {
|
||||
return new Invariant[] {
|
||||
BRAND_SETTLEMENT_NON_NEGATIVE, BRAND_SETTLEMENT_FORMULA, BRAND_COMMISSION_FORMULA, BRAND_COMMISSION_RATE_DEFAULT,
|
||||
IRD_NON_NEGATIVE, IRD_FORMULA, IRD_RATE_DEFAULT,
|
||||
ISSUER_FEE_NON_NEGATIVE, ISSUER_FEE_FORMULA, ISSUER_FEE_RATE_DEFAULT, ISSUER_SETTLEMENT_NON_NEGATIVE, ISSUER_SETTLEMENT_FORMULA,
|
||||
AMOUNT_NOT_NULL, AMOUNT_NON_NEGATIVE, AMOUNT_MAX_LIMIT,
|
||||
AMOUNT_SCALE, ROUNDING_MODE
|
||||
};
|
||||
}
|
||||
|
||||
public static class Invariant {
|
||||
private final String id;
|
||||
private final String description;
|
||||
private final String formula;
|
||||
private final String affectedClass;
|
||||
private final String preservationNote;
|
||||
|
||||
public Invariant(String id, String description, String formula, String affectedClass, String preservationNote) {
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
this.formula = formula;
|
||||
this.affectedClass = affectedClass;
|
||||
this.preservationNote = preservationNote;
|
||||
}
|
||||
|
||||
public String getId() { return id; }
|
||||
public String getDescription() { return description; }
|
||||
public String getFormula() { return formula; }
|
||||
public String getAffectedClass() { return affectedClass; }
|
||||
public String getPreservationNote() { return preservationNote; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.bais.card.settlement;
|
||||
|
||||
/**
|
||||
* Settlement Invariants Table Generator
|
||||
* Markdown 형식의 불변식 표 생성
|
||||
*/
|
||||
public class SettlementInvariantTable {
|
||||
|
||||
public static String generateMarkdownTable() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("# 카드 정산 불변식 표 (Card Settlement Invariants)\n\n");
|
||||
sb.append("**범위**: card-settlement-migration\n\n");
|
||||
|
||||
sb.append("## 1. 브랜드 정산 불변식\n\n");
|
||||
sb.append("| ID | 설명 | 수식 | 영향 클래스 | 보존 주의사항 |\n");
|
||||
sb.append("|---|---|---|---|---|\n");
|
||||
for (SettlementInvariant.Invariant inv : SettlementInvariant.getAllInvariants()) {
|
||||
if (inv.getId().startsWith("BRAND_SETTLEMENT") || inv.getId().startsWith("BRAND_COMMISSION")) {
|
||||
sb.append(String.format("| %s | %s | %s | %s | %s |\n",
|
||||
inv.getId(), inv.getDescription(), inv.getFormula(),
|
||||
inv.getAffectedClass(), inv.getPreservationNote()));
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("\n## 2. IRD 불변식\n\n");
|
||||
sb.append("| ID | 설명 | 수식 | 영향 클래스 | 보존 주의사항 |\n");
|
||||
sb.append("|---|---|---|---|---|\n");
|
||||
for (SettlementInvariant.Invariant inv : SettlementInvariant.getAllInvariants()) {
|
||||
if (inv.getId().startsWith("IRD")) {
|
||||
sb.append(String.format("| %s | %s | %s | %s | %s |\n",
|
||||
inv.getId(), inv.getDescription(), inv.getFormula(),
|
||||
inv.getAffectedClass(), inv.getPreservationNote()));
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("\n## 3. 발급사 수수료 불변식\n\n");
|
||||
sb.append("| ID | 설명 | 수식 | 영향 클래스 | 보존 주의사항 |\n");
|
||||
sb.append("|---|---|---|---|---|\n");
|
||||
for (SettlementInvariant.Invariant inv : SettlementInvariant.getAllInvariants()) {
|
||||
if (inv.getId().startsWith("ISSUER")) {
|
||||
sb.append(String.format("| %s | %s | %s | %s | %s |\n",
|
||||
inv.getId(), inv.getDescription(), inv.getFormula(),
|
||||
inv.getAffectedClass(), inv.getPreservationNote()));
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("\n## 4. 공통 금액 검증 불변식\n\n");
|
||||
sb.append("| ID | 설명 | 수식 | 영향 클래스 | 보존 주의사항 |\n");
|
||||
sb.append("|---|---|---|---|---|\n");
|
||||
for (SettlementInvariant.Invariant inv : SettlementInvariant.getAllInvariants()) {
|
||||
if (inv.getId().startsWith("AMOUNT")) {
|
||||
sb.append(String.format("| %s | %s | %s | %s | %s |\n",
|
||||
inv.getId(), inv.getDescription(), inv.getFormula(),
|
||||
inv.getAffectedClass(), inv.getPreservationNote()));
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("\n## 5. 금액 정밀도 불변식\n\n");
|
||||
sb.append("| ID | 설명 | 수식 | 영향 클래스 | 보존 주의사항 |\n");
|
||||
sb.append("|---|---|---|---|---|\n");
|
||||
for (SettlementInvariant.Invariant inv : SettlementInvariant.getAllInvariants()) {
|
||||
if (inv.getId().equals("AMOUNT_SCALE") || inv.getId().equals("ROUNDING_MODE")) {
|
||||
sb.append(String.format("| %s | %s | %s | %s | %s |\n",
|
||||
inv.getId(), inv.getDescription(), inv.getFormula(),
|
||||
inv.getAffectedClass(), inv.getPreservationNote()));
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(generateMarkdownTable());
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package com.bais.card.settlement;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* 불변식 검증 테스트
|
||||
* SettlementInvariant에 정의된 모든 불변식이 Calculator에서 올바르게 구현되었는지 검증
|
||||
*/
|
||||
@DisplayName("불변식 검증 테스트")
|
||||
class SettlementInvariantVerificationTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("모든 불변식이 정의되어 있어야 함")
|
||||
void testAllInvariantsDefined() {
|
||||
SettlementInvariant.Invariant[] invariants = SettlementInvariant.getAllInvariants();
|
||||
assertNotNull(invariants);
|
||||
assertEquals(17, invariants.length, "총 17개의 불변식이 정의되어야 함");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("불변식 ID는 고유해야 함")
|
||||
void testInvariantIdsAreUnique() {
|
||||
long uniqueCount = Arrays.stream(SettlementInvariant.getAllInvariants())
|
||||
.map(SettlementInvariant.Invariant::getId)
|
||||
.distinct()
|
||||
.count();
|
||||
assertEquals(17, uniqueCount, "모든 불변식 ID는 고유해야 함");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("주요 불변식 존재 확인")
|
||||
void testKeyInvariantsExist() {
|
||||
Set<String> ids = Arrays.stream(SettlementInvariant.getAllInvariants())
|
||||
.map(SettlementInvariant.Invariant::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
assertTrue(ids.contains("BRAND_SETTLEMENT_NON_NEGATIVE"));
|
||||
assertTrue(ids.contains("BRAND_SETTLEMENT_FORMULA"));
|
||||
assertTrue(ids.contains("BRAND_COMMISSION_FORMULA"));
|
||||
assertTrue(ids.contains("BRAND_COMMISSION_RATE_DEFAULT"));
|
||||
assertTrue(ids.contains("IRD_NON_NEGATIVE"));
|
||||
assertTrue(ids.contains("IRD_FORMULA"));
|
||||
assertTrue(ids.contains("IRD_RATE_DEFAULT"));
|
||||
assertTrue(ids.contains("ISSUER_FEE_NON_NEGATIVE"));
|
||||
assertTrue(ids.contains("ISSUER_FEE_FORMULA"));
|
||||
assertTrue(ids.contains("ISSUER_FEE_RATE_DEFAULT"));
|
||||
assertTrue(ids.contains("ISSUER_SETTLEMENT_NON_NEGATIVE"));
|
||||
assertTrue(ids.contains("ISSUER_SETTLEMENT_FORMULA"));
|
||||
assertTrue(ids.contains("AMOUNT_NOT_NULL"));
|
||||
assertTrue(ids.contains("AMOUNT_NON_NEGATIVE"));
|
||||
assertTrue(ids.contains("AMOUNT_MAX_LIMIT"));
|
||||
assertTrue(ids.contains("AMOUNT_SCALE"));
|
||||
assertTrue(ids.contains("ROUNDING_MODE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("불변식 설명과 수식이 비어있지 않아야 함")
|
||||
void testInvariantMetadataNotEmpty() {
|
||||
for (SettlementInvariant.Invariant inv : SettlementInvariant.getAllInvariants()) {
|
||||
assertNotNull(inv.getDescription());
|
||||
assertFalse(inv.getDescription().isEmpty());
|
||||
assertNotNull(inv.getFormula());
|
||||
assertFalse(inv.getFormula().isEmpty());
|
||||
assertNotNull(inv.getAffectedClass());
|
||||
assertFalse(inv.getAffectedClass().isEmpty());
|
||||
assertNotNull(inv.getPreservationNote());
|
||||
assertFalse(inv.getPreservationNote().isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue