- 기존 광고 차단(if !strong && AD_HINT)은 강신호가 보장된 지점이라
항상 false → 한 번도 실행되지 않는 죽은 코드였음. 광고어가 섞여도
승인/입금 등 신호가 있으면 그대로 등록되던 원인.
- 광고어를 2단계로 분리:
- AD_BLOCK(이벤트·쿠폰·혜택·당첨 등): 거래신호가 있어도 무조건 제외
- AD_WEAK(적립·포인트·할인 등): '결제'만 있고 명확한 거래신호가 없을 때 제외
(실거래 영수증의 '포인트 적립'은 보존)
- '결제'는 광고 문구에도 흔해 약신호(weakApproval)로 강등
- 파서 단위테스트 +4 (광고+강신호 차단, 실거래 포인트 보존, 결제단독 광고/정상)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
129 lines
4.8 KiB
Java
129 lines
4.8 KiB
Java
package com.sb.web.account.service;
|
|
|
|
import org.junit.jupiter.api.DisplayName;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import java.time.LocalDate;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
|
|
/**
|
|
* 결제/이체 알림 파서 단위테스트.
|
|
* 카드: 승인→지출 / 취소→수입(환불). 은행: 입금→수입 / 출금·이체→지출. 광고 차단.
|
|
*/
|
|
class CardNotificationParserTest {
|
|
|
|
private final CardNotificationParser parser = new CardNotificationParser();
|
|
private final LocalDate today = LocalDate.of(2026, 6, 6);
|
|
|
|
@Test
|
|
@DisplayName("카드 승인 = 지출(카드)")
|
|
void cardApprovalIsExpense() {
|
|
var p = parser.parse("[Web발신]", "삼성카드 승인 50,000원 일시불\n스타벅스 06/06 14:30", today);
|
|
assertThat(p.isRecognized()).isTrue();
|
|
assertThat(p.isIncome()).isFalse();
|
|
assertThat(p.isBankTransfer()).isFalse();
|
|
assertThat(p.getIssuer()).isEqualTo("삼성");
|
|
assertThat(p.getAmount()).isEqualTo(50000L);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("카드 승인취소 = 수입(환불, 카드)")
|
|
void cardCancelIsIncome() {
|
|
var p = parser.parse("현대카드", "승인취소 12,000원", today);
|
|
assertThat(p.isRecognized()).isTrue();
|
|
assertThat(p.isIncome()).isTrue();
|
|
assertThat(p.isBankTransfer()).isFalse();
|
|
assertThat(p.getIssuer()).isEqualTo("현대");
|
|
assertThat(p.getAmount()).isEqualTo(12000L);
|
|
}
|
|
|
|
@Test
|
|
@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 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("광고성 푸시(강신호 없음)는 무시")
|
|
void blocksAdvertisement() {
|
|
var p = parser.parse("카카오", "혜택 쿠폰 10,000원 적립 이벤트", today);
|
|
assertThat(p.isRecognized()).isFalse();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("승인 신호가 있어도 명백한 광고어(이벤트/쿠폰/혜택)면 무시")
|
|
void blocksAdEvenWithSignal() {
|
|
var p = parser.parse("[Web발신]", "삼성카드 승인 9,900원 이벤트 쿠폰 혜택 받으세요", today);
|
|
assertThat(p.isRecognized()).isFalse();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("실거래(승인)에 포인트/적립이 붙어도 인식 유지")
|
|
void keepsRealTxnWithPoints() {
|
|
var p = parser.parse("신한카드", "승인 50,000원 스타벅스 포인트 적립 120P", today);
|
|
assertThat(p.isRecognized()).isTrue();
|
|
assertThat(p.isIncome()).isFalse();
|
|
assertThat(p.getAmount()).isEqualTo(50000L);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("'결제'만 있고 약한 광고어(적립/포인트)면 광고로 무시")
|
|
void blocksPaymentOnlyAd() {
|
|
var p = parser.parse("[Web발신]", "5,000원 결제하고 포인트 적립 받기", today);
|
|
assertThat(p.isRecognized()).isFalse();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("'결제'만 있어도 광고어 없으면 지출로 인식")
|
|
void keepsPaymentOnlyWhenNoAd() {
|
|
var p = parser.parse("롯데카드", "결제 3,000원 편의점", today);
|
|
assertThat(p.isRecognized()).isTrue();
|
|
assertThat(p.isIncome()).isFalse();
|
|
assertThat(p.isBankTransfer()).isFalse();
|
|
assertThat(p.getAmount()).isEqualTo(3000L);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("금액 없으면 무시")
|
|
void requiresAmount() {
|
|
assertThat(parser.parse("삼성카드", "승인 완료되었습니다", today).isRecognized()).isFalse();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("승인/입금/출금 등 신호 없으면 무시")
|
|
void requiresSignal() {
|
|
assertThat(parser.parse("삼성카드", "50,000원", today).isRecognized()).isFalse();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("빈 알림은 무시")
|
|
void blankIsNotRecognized() {
|
|
assertThat(parser.parse(null, null, today).isRecognized()).isFalse();
|
|
assertThat(parser.parse("", "", today).isRecognized()).isFalse();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("MM/DD 날짜를 올해 거래일로 추정")
|
|
void guessesDate() {
|
|
var p = parser.parse("신한카드", "승인 5,000원 6/6", today);
|
|
assertThat(p.isRecognized()).isTrue();
|
|
assertThat(p.getDate()).isEqualTo(LocalDate.of(2026, 6, 6));
|
|
}
|
|
}
|