feat: 가입정보 변경·비밀번호 재인증 API + 백엔드 단위테스트·CI 게이트

- POST /auth/verify-password, PUT /auth/profile(세션 표시 이름 동기화)
- MemberMapper.updateProfile, AuthSessionMapper.updateName
- 단위테스트(24): CardNotificationParser(8), AuthService(16, Mockito)
- CI(.gitea): 테스트 리포트 아티팩트 업로드(clean build가 곧 테스트 게이트)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-06 14:08:01 +09:00
co-authored by Claude Opus 4.8
parent 5e2bdae37d
commit e592c174bf
11 changed files with 527 additions and 0 deletions
@@ -0,0 +1,90 @@
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 parsesApprovedPayment() {
var p = parser.parse("[Web발신]", "삼성카드 승인 50,000원 일시불\n스타벅스 06/06 14:30", today);
assertThat(p.isCard()).isTrue();
assertThat(p.isCanceled()).isFalse();
assertThat(p.getIssuer()).isEqualTo("삼성");
assertThat(p.getAmount()).isEqualTo(50000L);
assertThat(p.getNotifKey()).isNotBlank();
}
@Test
@DisplayName("승인취소는 취소 결제로 인식한다")
void parsesCanceledPayment() {
var p = parser.parse("현대카드", "승인취소 12,000원", today);
assertThat(p.isCard()).isTrue();
assertThat(p.isCanceled()).isTrue();
assertThat(p.getIssuer()).isEqualTo("현대");
assertThat(p.getAmount()).isEqualTo(12000L);
}
@Test
@DisplayName("강한 승인신호 없이 광고성 표현만 있으면 결제로 보지 않는다")
void blocksAdvertisementPush() {
var p = parser.parse("카카오", "결제하고 혜택 받기! 10,000원 쿠폰 적립 이벤트", today);
assertThat(p.isCard()).isFalse();
}
@Test
@DisplayName("승인 신호가 있으면 광고 표현이 섞여도 결제로 인식한다")
void strongSignalOverridesAdWords() {
var p = parser.parse("삼성카드", "승인 30,000원 (이벤트 적립 대상)", today);
assertThat(p.isCard()).isTrue();
assertThat(p.getAmount()).isEqualTo(30000L);
}
@Test
@DisplayName("금액이 없으면 결제 알림이 아니다")
void requiresAmount() {
var p = parser.parse("삼성카드", "승인 완료되었습니다", today);
assertThat(p.isCard()).isFalse();
}
@Test
@DisplayName("승인/취소 키워드가 없으면 결제 알림이 아니다")
void requiresApprovalKeyword() {
var p = parser.parse("삼성카드", "50,000원", today);
assertThat(p.isCard()).isFalse();
}
@Test
@DisplayName("빈 알림은 결제가 아니다")
void blankIsNotCard() {
assertThat(parser.parse(null, null, today).isCard()).isFalse();
assertThat(parser.parse("", "", today).isCard()).isFalse();
}
@Test
@DisplayName("MM/DD 날짜를 올해 거래일로 추정한다")
void guessesDate() {
var p = parser.parse("신한카드", "승인 5,000원 6/6", today);
assertThat(p.isCard()).isTrue();
assertThat(p.getDate()).isEqualTo(LocalDate.of(2026, 6, 6));
}
}