feat: 알림 분류 — 카드 승인/취소 + 은행 입금/출금·이체 구분
- 파서: 입금→수입, 출금/이체→지출, 카드 승인→지출, 카드 취소/환불→수입(환불) 은행 거래는 은행(BANK) 지갑에, 카드는 카드(CARD) 지갑에 매칭(matchWalletId 일반화) - createFromNotification: 파싱된 수입/지출 타입·지갑종류로 생성 - 파서 테스트 갱신(은행 입금/출금 케이스 포함) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
55c47a43e6
commit
31590343cf
@@ -8,8 +8,8 @@ import java.time.LocalDate;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* 카드 결제 알림 파서 단위테스트.
|
||||
* 핵심: 정상 결제 인식 / 취소 인식 / 광고성 푸시 차단 / 강신호 우선.
|
||||
* 결제/이체 알림 파서 단위테스트.
|
||||
* 카드: 승인→지출 / 취소→수입(환불). 은행: 입금→수입 / 출금·이체→지출. 광고 차단.
|
||||
*/
|
||||
class CardNotificationParserTest {
|
||||
|
||||
@@ -17,74 +17,79 @@ class CardNotificationParserTest {
|
||||
private final LocalDate today = LocalDate.of(2026, 6, 6);
|
||||
|
||||
@Test
|
||||
@DisplayName("정상 승인 결제는 카드로 인식하고 카드사·금액을 추출한다")
|
||||
void parsesApprovedPayment() {
|
||||
@DisplayName("카드 승인 = 지출(카드)")
|
||||
void cardApprovalIsExpense() {
|
||||
var p = parser.parse("[Web발신]", "삼성카드 승인 50,000원 일시불\n스타벅스 06/06 14:30", today);
|
||||
|
||||
assertThat(p.isCard()).isTrue();
|
||||
assertThat(p.isCanceled()).isFalse();
|
||||
assertThat(p.isRecognized()).isTrue();
|
||||
assertThat(p.isIncome()).isFalse();
|
||||
assertThat(p.isBankTransfer()).isFalse();
|
||||
assertThat(p.getIssuer()).isEqualTo("삼성");
|
||||
assertThat(p.getAmount()).isEqualTo(50000L);
|
||||
assertThat(p.getNotifKey()).isNotBlank();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("승인취소는 취소 결제로 인식한다")
|
||||
void parsesCanceledPayment() {
|
||||
@DisplayName("카드 승인취소 = 수입(환불, 카드)")
|
||||
void cardCancelIsIncome() {
|
||||
var p = parser.parse("현대카드", "승인취소 12,000원", today);
|
||||
|
||||
assertThat(p.isCard()).isTrue();
|
||||
assertThat(p.isCanceled()).isTrue();
|
||||
assertThat(p.isRecognized()).isTrue();
|
||||
assertThat(p.isIncome()).isTrue();
|
||||
assertThat(p.isBankTransfer()).isFalse();
|
||||
assertThat(p.getIssuer()).isEqualTo("현대");
|
||||
assertThat(p.getAmount()).isEqualTo(12000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("강한 승인신호 없이 광고성 표현만 있으면 결제로 보지 않는다")
|
||||
void blocksAdvertisementPush() {
|
||||
var p = parser.parse("카카오", "결제하고 혜택 받기! 10,000원 쿠폰 적립 이벤트", today);
|
||||
|
||||
assertThat(p.isCard()).isFalse();
|
||||
@DisplayName("은행 입금 = 수입(은행)")
|
||||
void bankDepositIsIncome() {
|
||||
var p = parser.parse("[Web발신]", "신한은행 입금 300,000원 홍길동", today);
|
||||
assertThat(p.isRecognized()).isTrue();
|
||||
assertThat(p.isIncome()).isTrue();
|
||||
assertThat(p.isBankTransfer()).isTrue();
|
||||
assertThat(p.getIssuer()).isEqualTo("신한");
|
||||
assertThat(p.getAmount()).isEqualTo(300000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("승인 신호가 있으면 광고 표현이 섞여도 결제로 인식한다")
|
||||
void strongSignalOverridesAdWords() {
|
||||
var p = parser.parse("삼성카드", "승인 30,000원 (이벤트 적립 대상)", today);
|
||||
|
||||
assertThat(p.isCard()).isTrue();
|
||||
assertThat(p.getAmount()).isEqualTo(30000L);
|
||||
@DisplayName("은행 출금/이체 = 지출(은행)")
|
||||
void bankWithdrawIsExpense() {
|
||||
var p = parser.parse("국민은행", "출금 45,000원 이체", today);
|
||||
assertThat(p.isRecognized()).isTrue();
|
||||
assertThat(p.isIncome()).isFalse();
|
||||
assertThat(p.isBankTransfer()).isTrue();
|
||||
assertThat(p.getAmount()).isEqualTo(45000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("금액이 없으면 결제 알림이 아니다")
|
||||
@DisplayName("광고성 푸시(강신호 없음)는 무시")
|
||||
void blocksAdvertisement() {
|
||||
var p = parser.parse("카카오", "혜택 쿠폰 10,000원 적립 이벤트", today);
|
||||
assertThat(p.isRecognized()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("금액 없으면 무시")
|
||||
void requiresAmount() {
|
||||
var p = parser.parse("삼성카드", "승인 완료되었습니다", today);
|
||||
|
||||
assertThat(p.isCard()).isFalse();
|
||||
assertThat(parser.parse("삼성카드", "승인 완료되었습니다", today).isRecognized()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("승인/취소 키워드가 없으면 결제 알림이 아니다")
|
||||
void requiresApprovalKeyword() {
|
||||
var p = parser.parse("삼성카드", "50,000원", today);
|
||||
|
||||
assertThat(p.isCard()).isFalse();
|
||||
@DisplayName("승인/입금/출금 등 신호 없으면 무시")
|
||||
void requiresSignal() {
|
||||
assertThat(parser.parse("삼성카드", "50,000원", today).isRecognized()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("빈 알림은 결제가 아니다")
|
||||
void blankIsNotCard() {
|
||||
assertThat(parser.parse(null, null, today).isCard()).isFalse();
|
||||
assertThat(parser.parse("", "", today).isCard()).isFalse();
|
||||
@DisplayName("빈 알림은 무시")
|
||||
void blankIsNotRecognized() {
|
||||
assertThat(parser.parse(null, null, today).isRecognized()).isFalse();
|
||||
assertThat(parser.parse("", "", today).isRecognized()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("MM/DD 날짜를 올해 거래일로 추정한다")
|
||||
@DisplayName("MM/DD 날짜를 올해 거래일로 추정")
|
||||
void guessesDate() {
|
||||
var p = parser.parse("신한카드", "승인 5,000원 6/6", today);
|
||||
|
||||
assertThat(p.isCard()).isTrue();
|
||||
assertThat(p.isRecognized()).isTrue();
|
||||
assertThat(p.getDate()).isEqualTo(LocalDate.of(2026, 6, 6));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user