-
Notifications
You must be signed in to change notification settings - Fork 0
[FEAT] 환불 기한 1년으로 제한 #346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEAT] 환불 기한 1년으로 제한 #346
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,6 +54,8 @@ class TokenControllerTest extends BaseControllerTest { | |
| private TokenService tokenService; | ||
| @Autowired | ||
| private TokenPurchaseRepository tokenPurchaseRepository; | ||
| @Autowired | ||
| private org.springframework.jdbc.core.JdbcTemplate jdbcTemplate; | ||
|
Comment on lines
+57
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial 타입 선언은 import를 사용하면 가독성이 더 좋습니다. 필드에서 FQCN 직접 사용 대신 import로 정리하면 테스트 클래스 읽기가 수월해집니다. 🤖 Prompt for AI Agents |
||
|
|
||
| @Test | ||
| void 토큰_구매_DTO_검증_실패() throws Exception { | ||
|
|
@@ -628,6 +630,42 @@ class TokenControllerTest extends BaseControllerTest { | |
| )); | ||
| } | ||
|
|
||
| @Test | ||
| void 구매일로부터_1년_경과한_토큰_환불_실패() throws Exception { | ||
| // given | ||
| Member member = memberRepository.save(MemberFixtureBuilder.builder().build()); | ||
| tokenService.createTokensForNewMember(member.getId()); | ||
|
|
||
| TokenPurchase tokenPurchase = tokenPurchaseRepository.save( | ||
| TokenPurchaseFixtureBuilder.builder() | ||
| .memberId(member.getId()) | ||
| .count(10) | ||
| .remainingCount(10) | ||
| .state(TokenPurchaseState.REFUNDABLE) | ||
| .build() | ||
| ); | ||
|
|
||
| // created_at을 1년 전으로 변경 (native SQL로 @CreatedDate 필드 직접 수정) | ||
| jdbcTemplate.update( | ||
| "UPDATE token_purchase SET created_at = ? WHERE id = ?", | ||
| java.time.LocalDateTime.now().minusYears(1).minusDays(1), | ||
| tokenPurchase.getId() | ||
| ); | ||
|
|
||
| MockHttpSession session = new MockHttpSession(); | ||
| session.setAttribute("MEMBER_ID", member.getId()); | ||
|
|
||
| TokenRefundRequest request = new TokenRefundRequest(RefundReasonCode.CHANGE_OF_MIND, null); | ||
|
|
||
| // when & then | ||
| mockMvc.perform(patch("/api/v1/token-purchases/{tokenPurchaseId}/refund", tokenPurchase.getId()) | ||
| .contentType(MediaType.APPLICATION_JSON) | ||
| .content(objectMapper.writeValueAsString(request)) | ||
| .header("Cookie", "JSESSIONID=" + session.getId()) | ||
| .session(session)) | ||
| .andExpect(status().isBadRequest()); | ||
| } | ||
|
Comment on lines
+633
to
+667
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial 실패 원인까지 검증해 테스트 의도를 고정해 주세요. 현재는 🔧 보강 예시 mockMvc.perform(patch("/api/v1/token-purchases/{tokenPurchaseId}/refund", tokenPurchase.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.header("Cookie", "JSESSIONID=" + session.getId())
.session(session))
- .andExpect(status().isBadRequest());
+ .andExpect(status().isBadRequest())
+ .andExpect(result -> assertThat(result.getResponse().getContentAsString())
+ .contains("구매일로부터 1년이 경과하여 환불이 불가합니다."));🤖 Prompt for AI Agents |
||
|
|
||
| @Test | ||
| void 타인의_토큰_환불_실패() throws Exception { | ||
| // given | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package com.samhap.kokomen.token.domain; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
|
|
||
| import com.samhap.kokomen.global.fixture.token.TokenPurchaseFixtureBuilder; | ||
| import java.lang.reflect.Field; | ||
| import java.time.LocalDateTime; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| class TokenPurchaseTest { | ||
|
|
||
| @Test | ||
| void 구매일로부터_1년_이내이면_환불_가능하다() { | ||
| // given | ||
| TokenPurchase tokenPurchase = TokenPurchaseFixtureBuilder.builder() | ||
| .state(TokenPurchaseState.REFUNDABLE) | ||
| .count(10) | ||
| .remainingCount(10) | ||
| .build(); | ||
| setCreatedAt(tokenPurchase, LocalDateTime.now().minusMonths(6)); | ||
|
|
||
| // when & then | ||
| assertThat(tokenPurchase.isRefundable()).isTrue(); | ||
| assertThat(tokenPurchase.isRefundExpired()).isFalse(); | ||
| } | ||
|
|
||
| @Test | ||
| void 구매일로부터_1년이_경과하면_환불_불가능하다() { | ||
| // given | ||
| TokenPurchase tokenPurchase = TokenPurchaseFixtureBuilder.builder() | ||
| .state(TokenPurchaseState.REFUNDABLE) | ||
| .count(10) | ||
| .remainingCount(10) | ||
| .build(); | ||
| setCreatedAt(tokenPurchase, LocalDateTime.now().minusYears(1).minusDays(1)); | ||
|
|
||
| // when & then | ||
| assertThat(tokenPurchase.isRefundable()).isFalse(); | ||
| assertThat(tokenPurchase.isRefundExpired()).isTrue(); | ||
| } | ||
|
|
||
| @Test | ||
| void 구매일로부터_정확히_1년이면_환불_가능하다() { | ||
| // given | ||
| TokenPurchase tokenPurchase = TokenPurchaseFixtureBuilder.builder() | ||
| .state(TokenPurchaseState.REFUNDABLE) | ||
| .count(10) | ||
| .remainingCount(10) | ||
| .build(); | ||
| setCreatedAt(tokenPurchase, LocalDateTime.now().minusYears(1).plusMinutes(1)); | ||
|
Comment on lines
+43
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 테스트 이름과 설정 시간이 불일치합니다.
🔧 수정 예시- void 구매일로부터_정확히_1년이면_환불_가능하다() {
+ void 구매일로부터_1년_미만이면_환불_가능하다() {
...
- setCreatedAt(tokenPurchase, LocalDateTime.now().minusYears(1).plusMinutes(1));
+ setCreatedAt(tokenPurchase, LocalDateTime.now().minusYears(1).plusMinutes(1));또는 - setCreatedAt(tokenPurchase, LocalDateTime.now().minusYears(1).plusMinutes(1));
+ setCreatedAt(tokenPurchase, LocalDateTime.now().minusYears(1));🤖 Prompt for AI Agents |
||
|
|
||
| // when & then | ||
| assertThat(tokenPurchase.isRefundable()).isTrue(); | ||
| assertThat(tokenPurchase.isRefundExpired()).isFalse(); | ||
| } | ||
|
|
||
| private void setCreatedAt(TokenPurchase tokenPurchase, LocalDateTime createdAt) { | ||
| try { | ||
| Field field = tokenPurchase.getClass().getSuperclass().getDeclaredField("createdAt"); | ||
| field.setAccessible(true); | ||
| field.set(tokenPurchase, createdAt); | ||
| } catch (NoSuchFieldException | IllegalAccessException e) { | ||
| throw new RuntimeException("createdAt 필드 설정 실패", e); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
도메인 모델 내에서
LocalDateTime.now()를 직접 호출하면 시스템 시계에 대한 숨은 의존성이 생겨 테스트하기 어려운 코드가 될 수 있습니다. 시간과 관련된 로직은 외부에서 현재 시간을 주입받는 형태로 변경하는 것을 고려해보세요. 이렇게 하면 테스트 시 시간을 제어하기 용이해져 코드의 안정성과 예측 가능성을 높일 수 있습니다.예를 들어,
isRefundable과isRefundExpired메서드가LocalDateTime을 인자로 받도록 수정하고, 서비스 레이어에서LocalDateTime.now()를 전달하는 방식입니다. 이 변경은isNotRefundable등 관련 메서드들의 시그니처 변경을 수반합니다. 이렇게 수정하면TokenPurchaseTest에서 리플렉션을 사용하여createdAt을 설정하는 대신, 테스트 시간을 명시적으로 전달하여 더 깔끔한 테스트를 작성할 수 있습니다.