Skip to content
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

[fix] 추첨 이벤트 참여 대상이 아닌 이벤트 유저가 참여할 수 있는 문제 수정(#107) #109

Merged
merged 2 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons

Auth classAnnotation = handlerMethod.getBeanType().getAnnotation(Auth.class);
Auth methodAnnotation = handlerMethod.getMethod().getAnnotation(Auth.class);
log.info("annotation {} {}", methodAnnotation, classAnnotation);

// 인증 필요한지 검사. 어노테이션 없으면 인증 필요 없음
if (classAnnotation == null && methodAnnotation == null) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ public boolean isEnded(LocalDateTime now) {
@JoinColumn(name="event_frame_id")
private EventFrame eventFrame;

// eventFrame을 거치지 않고 유저를 확인하기 위해 사용
@Column(name = "event_frame_id", updatable = false, insertable = false)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이벤트 프레임을 fetch하는 대신 외래키끼리 비교할 수 있도록 외래키를 노출했습니다. 단, 외래키와 JoinColumn이 동시에 존재할 때 둘 모두로 업데이트하려고하면 예외가 발생하므로 외래키로는 업데이트나 삽입 동작이 불가능하도록 false로 지정했습니다.

private Long eventFrameId;

// 원래는 one-to-one 관계이지만, JPA 동작에 의해 강제로 EAGER FETCH로 처리돰 -> one-to-many 로 관리
@OneToMany(mappedBy = "eventMetadata", cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
private final List<DrawEvent> drawEventList = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,13 @@ protected void participateAtDate(String eventId, String eventUserId, LocalDateTi
if(event.getEventType() != EventType.draw) throw new EventException(ErrorCode.EVENT_NOT_FOUND);

DrawEvent drawEvent = event.getDrawEvent();
if(drawEvent == null) throw new EventException(ErrorCode.EVENT_NOT_FOUND);

EventUser eventUser = eventUserRepository.findByUserId(eventUserId)
.orElseThrow(() -> new EventUserException(ErrorCode.USER_NOT_FOUND));

if(!eventUser.getEventFrameId().equals(event.getEventFrameId())) throw new EventException(ErrorCode.CANNOT_PARTICIPATE);

LocalDate today = date.toLocalDate();

// 이벤트 기간 안에 있는지 검사
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public class EventUser {
@JoinColumn(name = "event_frame_id")
private EventFrame eventFrame;

// eventFrame을 거치지 않고 이벤트를 매칭하기 위해 사용
@Column(name = "event_frame_id", updatable = false, insertable = false)
private Long eventFrameId;

@OneToMany(mappedBy = "eventUser")
private List<Comment> commentList = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package hyundai.softeer.orange.event.draw.service;

import hyundai.softeer.orange.common.ErrorCode;
import hyundai.softeer.orange.event.common.entity.EventMetadata;
import hyundai.softeer.orange.event.common.enums.EventType;
import hyundai.softeer.orange.event.common.exception.EventException;
Expand All @@ -25,14 +26,15 @@
import static org.mockito.Mockito.*;

class EventParticipationServiceTest {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중복을 제거하고, 의도와는 다르게 동작하던 테스트 코드를 수정했습니다. instanceOf와 hasMessage을 추가하여 기존 테스트코드에 문제가 있음을 식별하고 수정했습니다.

EventParticipationInfoRepository epiRepository = mock(EventParticipationInfoRepository.class);
EventUserRepository euRepository = mock(EventUserRepository.class);
Long frameId = 1L;

@DisplayName("이벤트가 존재하지 않으면 예외 반환")
@Test
void getParticipationDateList_throwIfEventNotExist() {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.empty());
// EventParticipationInfoRepository epiRepository = mock(EventParticipationInfoRepository.class);
// EventUserRepository euRepository = mock(EventUserRepository.class);

EventParticipationService service = new EventParticipationService(null, emRepository, null);

assertThatThrownBy(() -> {
Expand All @@ -43,7 +45,6 @@ void getParticipationDateList_throwIfEventNotExist() {
@DisplayName("이벤트가 존재해도, draw 이벤트가 아니면 예외 반환")
@Test
void getParticipationDateList_EventIsNotDraw() {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(
EventMetadata.builder().eventType(EventType.fcfs).build()
));
Expand All @@ -58,7 +59,6 @@ void getParticipationDateList_EventIsNotDraw() {
@DisplayName("이벤트가 draw 이벤트여도 drawEvent == null이면 예외 반환")
@Test
void getParticipationDateList_EventIsDrawButDrawEventIsNull() {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(
EventMetadata.builder().eventType(EventType.draw).build()
));
Expand All @@ -77,15 +77,12 @@ void getParticipationDateList_returnParticipationInfos() {
.eventType(EventType.draw)
.build();
eventMetadata.updateDrawEvent(new DrawEvent());
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(eventMetadata));
var mockDto1 = mock(EventParticipationDateDto.class);
var mockDto2 = mock(EventParticipationDateDto.class);
when(mockDto1.getDate()).thenReturn(LocalDateTime.now());
when(mockDto2.getDate()).thenReturn(LocalDateTime.now());


EventParticipationInfoRepository epiRepository = mock(EventParticipationInfoRepository.class);
when(epiRepository.findByEventUserId(any(), any())).thenReturn(List.of(mockDto1, mockDto2));

EventParticipationService service = new EventParticipationService(epiRepository, emRepository, null);
Expand All @@ -97,20 +94,18 @@ void getParticipationDateList_returnParticipationInfos() {
@DisplayName("이벤트가 존재하지 않으면 예외 반환")
@Test
void participateAtDaily_throwIfEventNotExist() {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.empty());

EventParticipationService service = new EventParticipationService(null, emRepository, null);

assertThatThrownBy(() -> {
service.participateDaily("test", "any");
});
}).hasMessage(ErrorCode.EVENT_NOT_FOUND.getMessage());
}

@DisplayName("이벤트가 존재해도, draw 이벤트가 아니면 예외 반환")
@Test
void participateAtDaily_EventIsNotDraw() {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(
EventMetadata.builder().eventType(EventType.fcfs).build()
));
Expand All @@ -119,14 +114,13 @@ void participateAtDaily_EventIsNotDraw() {

assertThatThrownBy(() -> {
service.participateDaily("test", "any");
});
}).hasMessage(ErrorCode.EVENT_NOT_FOUND.getMessage());
verify(emRepository, atLeastOnce()).findFirstByEventId(any());
}

@DisplayName("이벤트가 draw 이벤트여도 drawEvent == null이면 예외 반환")
@Test
void participateDaily_EventIsDrawButDrawEventIsNull() {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(
EventMetadata.builder().eventType(EventType.draw).build()
));
Expand All @@ -135,108 +129,114 @@ void participateDaily_EventIsDrawButDrawEventIsNull() {

assertThatThrownBy(() -> {
service.participateDaily("test", "any");
});
}).isInstanceOf(EventException.class)
.hasMessage(ErrorCode.EVENT_NOT_FOUND.getMessage());
}

@DisplayName("이벤트 유저가 없다면 예외 반환")
@Test
void participateDaily_throwIfNotEventTime() {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(
EventMetadata.builder()
.eventType(EventType.draw)
.build()
));
EventParticipationInfoRepository epiRepository = mock(EventParticipationInfoRepository.class);
var eventMetadata = EventMetadata.builder()
.eventType(EventType.draw)
.eventFrameId(frameId)
.build();
eventMetadata.updateDrawEvent(new DrawEvent());
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(eventMetadata));

EventUserRepository euRepository = mock(EventUserRepository.class);
when(euRepository.findByUserId(any())).thenReturn(Optional.empty());

EventParticipationService service = new EventParticipationService(epiRepository, emRepository, euRepository);
assertThatThrownBy(() -> {
service.participateDaily("test", "any");
}).isInstanceOf(EventUserException.class);
}).isInstanceOf(EventUserException.class)
.hasMessage(ErrorCode.USER_NOT_FOUND.getMessage());
verify(euRepository, atLeastOnce()).findByUserId(any());
}

@DisplayName("이벤트 유저가 있을 때 이벤트 기간이 지났으면 예외 반환")
@Test
void participateAtDate_throwIfNotEventTime() {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(
EventMetadata.builder()
.startTime(LocalDateTime.of(2024, 8, 1, 0, 0, 0))
.endTime(LocalDateTime.of(2024, 8, 10, 0, 0, 0))
.eventType(EventType.draw)
.build()
));
LocalDateTime now = LocalDateTime.of(2024, 9, 1, 0, 0, 0);
var eventMetadata = EventMetadata.builder()
.startTime(LocalDateTime.of(2024, 8, 1, 0, 0, 0))
.endTime(LocalDateTime.of(2024, 8, 10, 0, 0, 0))
.eventType(EventType.draw)
.eventFrameId(frameId)
.build();
eventMetadata.updateDrawEvent(new DrawEvent());

EventParticipationInfoRepository epiRepository = mock(EventParticipationInfoRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(eventMetadata));

LocalDateTime now = LocalDateTime.of(2024, 9, 1, 0, 0, 0);

EventUserRepository euRepository = mock(EventUserRepository.class);
// 유저 있음
when(euRepository.findByUserId(any())).thenReturn(Optional.of(mock(EventUser.class)));
EventUser user = mock(EventUser.class);
when(user.getEventFrameId()).thenReturn(frameId);
when(euRepository.findByUserId(any())).thenReturn(Optional.of(user));

EventParticipationService service = new EventParticipationService(epiRepository, emRepository, euRepository);
assertThatThrownBy(() -> {
service.participateAtDate("test", "any", now);
}).isInstanceOf(EventException.class);
}).isInstanceOf(EventException.class)
.hasMessage(ErrorCode.INVALID_EVENT_TIME.getMessage());
verify(epiRepository, never()).existsByEventUserAndDrawEventAndDateBetween(any(), any(), any(), any());
}

@DisplayName("오늘 참여했다면 예외 반환")
@Test
void participateAtDate_throwIfAlreadyParticipated() {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(
EventMetadata.builder()
.startTime(LocalDateTime.of(2024, 8, 1, 0, 0, 0))
.endTime(LocalDateTime.of(2024, 9, 2, 0, 0, 0))
.eventType(EventType.draw)
.build()
));
LocalDateTime now = LocalDateTime.of(2024, 9, 1, 0, 0, 0);
var eventMetadata = EventMetadata.builder()
.startTime(LocalDateTime.of(2024, 8, 1, 0, 0, 0))
.endTime(LocalDateTime.of(2024, 8, 10, 0, 0, 0))
.eventType(EventType.draw)
.eventFrameId(frameId)
.build();
eventMetadata.updateDrawEvent(new DrawEvent());
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(eventMetadata));

EventParticipationInfoRepository epiRepository = mock(EventParticipationInfoRepository.class);
// 이벤트 기간 내
LocalDateTime now = LocalDateTime.of(2024, 8, 3, 0, 0, 0);
// 오늘 참여함
when(epiRepository.existsByEventUserAndDrawEventAndDateBetween(any(), any(), any(), any())).thenReturn(true);

EventUserRepository euRepository = mock(EventUserRepository.class);
// 유저 있음
when(euRepository.findByUserId(any())).thenReturn(Optional.of(mock(EventUser.class)));
EventUser user = mock(EventUser.class);
when(user.getEventFrameId()).thenReturn(frameId);
when(euRepository.findByUserId(any())).thenReturn(Optional.of(user));

EventParticipationService service = new EventParticipationService(epiRepository, emRepository, euRepository);
assertThatThrownBy(() -> {
service.participateAtDate("test", "any", now);
}).isInstanceOf(EventException.class);
}).isInstanceOf(EventException.class)
.hasMessage(ErrorCode.ALREADY_PARTICIPATED.getMessage());
verify(epiRepository, atLeastOnce()).existsByEventUserAndDrawEventAndDateBetween(any(), any(), any(), any());
}

@DisplayName("오늘 처음 참여했다면 정상적으로 참여")
@Test
void participateAtDate_participateSuccessfully() {
EventMetadataRepository emRepository = mock(EventMetadataRepository.class);
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(
EventMetadata.builder()
.startTime(LocalDateTime.of(2024, 8, 1, 0, 0, 0))
.endTime(LocalDateTime.of(2024, 9, 2, 0, 0, 0))
.eventType(EventType.draw)
.build()
));
LocalDateTime now = LocalDateTime.of(2024, 9, 1, 0, 0, 0);
var eventMetadata = EventMetadata.builder()
.startTime(LocalDateTime.of(2024, 8, 1, 0, 0, 0))
.endTime(LocalDateTime.of(2024, 8, 10, 0, 0, 0))
.eventType(EventType.draw)
.eventFrameId(frameId)
.build();
eventMetadata.updateDrawEvent(new DrawEvent());
when(emRepository.findFirstByEventId(anyString())).thenReturn(Optional.of(eventMetadata));

EventParticipationInfoRepository epiRepository = mock(EventParticipationInfoRepository.class);
// 오늘 참여함
// 이벤트 기간 내
LocalDateTime now = LocalDateTime.of(2024, 8, 3, 0, 0, 0);
// 오늘 참여안함
when(epiRepository.existsByEventUserAndDrawEventAndDateBetween(any(), any(), any(), any())).thenReturn(false);

EventUserRepository euRepository = mock(EventUserRepository.class);
// 유저 있음
when(euRepository.findByUserId(any())).thenReturn(Optional.of(mock(EventUser.class)));
EventUser user = mock(EventUser.class);
when(user.getEventFrameId()).thenReturn(frameId);
when(euRepository.findByUserId(any())).thenReturn(Optional.of(user));
EventParticipationInfoRepository epiRepository = mock(EventParticipationInfoRepository.class);

EventParticipationService service = new EventParticipationService(epiRepository, emRepository, euRepository);
service.participateAtDate("test", "any", now);
verify(epiRepository, atLeastOnce()).existsByEventUserAndDrawEventAndDateBetween(any(), any(), any(), any());
verify(epiRepository, atLeastOnce()).save(any());
verify(epiRepository, times(1)).existsByEventUserAndDrawEventAndDateBetween(any(), any(), any(), any());
verify(epiRepository, times(1)).save(any());
}
}
Loading