Skip to content
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 @@ -256,7 +256,7 @@ public ResponseEntity<ApiResponseForm<List<PreparationDto>>> getPreparation(Http
content = @Content(
schema = @Schema(
type = "object",
example = "{\"scheduleId\": \"3fa85f64-5717-4562-b3fc-2c963f66afe5\", \"latenessTime\": 3}"
example = "{\"latenessTime\": 3}"
)
)
)
Expand All @@ -273,10 +273,11 @@ public ResponseEntity<ApiResponseForm<List<PreparationDto>>> getPreparation(Http
@PutMapping("/{scheduleId}/finish") // 약속 준비 종료 이후 지각시간(Schedule 테이블), 성실도 점수(User 테이블) 업데이트
public ResponseEntity<ApiResponseForm<?>> finishSchedule(
HttpServletRequest request,
@PathVariable UUID scheduleId,
@RequestBody FinishPreparationDto finishPreparationDto) {

Long userId = userAuthService.getUserIdFromToken(request);
scheduleService.finishSchedule(userId, finishPreparationDto);
scheduleService.finishSchedule(userId, scheduleId, finishPreparationDto);
String message = "지각시간과 성실도점수가 성공적으로 업데이트 되었습니다!";
return ResponseEntity.ok(ApiResponseForm.success(null, message));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public enum ErrorCode {
FIRST_PREPARATION_NOT_FOUND(1012, "해당 ID의 사용자의 준비과정을 찾을 수 없습니다.", HttpStatus.BAD_REQUEST),
NOTIFICATION_NOT_FOUND(1013, "알림을 찾을 수 없습니다.", HttpStatus.BAD_REQUEST ),
PREPARATION_ALREADY_EXISTS(1014, "해당 사용자의 준비과정이 이미 존재합니다.", HttpStatus.BAD_REQUEST),
SCHEDULE_ALREADY_FINISHED(1015, "이미 종료된 약속입니다.", HttpStatus.BAD_REQUEST),
SCHEDULE_ID_MISMATCH(1016, "경로의 scheduleId와 요청 본문의 scheduleId가 일치하지 않습니다.", HttpStatus.BAD_REQUEST),
ALARM_SETTINGS_INVALID_FIELD(1101, "ALARM_SETTINGS_INVALID_FIELD", HttpStatus.BAD_REQUEST),
ALARM_WINDOW_RANGE_TOO_LONG(1102, "ALARM_WINDOW_RANGE_TOO_LONG", HttpStatus.BAD_REQUEST),
DEVICE_SESSION_NOT_ACTIVE(1103, "DEVICE_SESSION_NOT_ACTIVE", HttpStatus.CONFLICT),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,20 +173,28 @@ public List<LatenessHistoryResponse> getLatenessHistory(Long userId) {

// 지각 시간 업데이트
@Transactional
public void updateLatenessTime(FinishPreparationDto finishPreparationDto) {
UUID scheduleId = finishPreparationDto.getScheduleId();
Integer latenessTime = finishPreparationDto.getLatenessTime();

Schedule schedule = scheduleRepository.findById(scheduleId)
.orElseThrow(() -> new GeneralException(SCHEDULE_NOT_FOUND));

public void updateLatenessTime(Schedule schedule, Integer latenessTime) {
schedule.updateLatenessTime(latenessTime);
scheduleRepository.save(schedule);
}

@Transactional
public void finishSchedule(Long userId, FinishPreparationDto finishPreparationDto) {
updateLatenessTime(finishPreparationDto);
public void finishSchedule(Long userId, UUID scheduleId, FinishPreparationDto finishPreparationDto) {
if (finishPreparationDto == null || finishPreparationDto.getLatenessTime() == null) {
throw new GeneralException(INVALID_INPUT);
}
if (finishPreparationDto.getScheduleId() != null && !scheduleId.equals(finishPreparationDto.getScheduleId())) {
throw new GeneralException(SCHEDULE_ID_MISMATCH);
}

Schedule schedule = getScheduleWithAuthorization(scheduleId, userId);
boolean alreadyFinishedByDoneStatus = schedule.getDoneStatus() != null && schedule.getDoneStatus() != DoneStatus.NOT_ENDED;
boolean alreadyFinishedByLatenessTime = schedule.getLatenessTime() != null && schedule.getLatenessTime() != -1;
if (alreadyFinishedByDoneStatus || alreadyFinishedByLatenessTime) {
throw new GeneralException(SCHEDULE_ALREADY_FINISHED);
}

updateLatenessTime(schedule, finishPreparationDto.getLatenessTime());
userService.updatePunctualityScore(userId, finishPreparationDto.getLatenessTime());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import devkor.ontime_back.TestSecurityConfig;
import devkor.ontime_back.dto.*;
import devkor.ontime_back.entity.DoneStatus;
import devkor.ontime_back.response.ErrorCode;
import devkor.ontime_back.response.GeneralException;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -261,16 +263,18 @@ void getLatenessHistory() throws Exception {
@Test
void finishSchedule() throws Exception {
// given
Long userId = 1L;
UUID scheduleId = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe5");
FinishPreparationDto finishPreparationDto = FinishPreparationDto.builder()
.scheduleId(UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe5"))
.latenessTime(0)
.build();

when(userAuthService.getUserIdFromToken(any())).thenReturn(1L);
doNothing().when(scheduleService).finishSchedule(any(Long.class), any(FinishPreparationDto.class));
when(userAuthService.getUserIdFromToken(any())).thenReturn(userId);
doNothing().when(scheduleService).finishSchedule(eq(userId), eq(scheduleId), any(FinishPreparationDto.class));

// when // then
mockMvc.perform(
put("/schedules/" + 1L + "/finish")
put("/schedules/{scheduleId}/finish", scheduleId)
.content(objectMapper.writeValueAsString(finishPreparationDto))
.contentType(MediaType.APPLICATION_JSON)
)
Expand All @@ -279,5 +283,35 @@ void finishSchedule() throws Exception {
.andExpect(jsonPath("$.code").value("200"))
.andExpect(jsonPath("$.status").value("success"))
.andExpect(jsonPath("$.message").value("지각시간과 성실도점수가 성공적으로 업데이트 되었습니다!"));

verify(scheduleService, times(1)).finishSchedule(eq(userId), eq(scheduleId), any(FinishPreparationDto.class));
}

@DisplayName("약속 종료 요청에서 경로와 본문의 scheduleId가 다르면 400을 반환한다.")
@Test
void finishSchedule_failByScheduleIdMismatch() throws Exception {
// given
Long userId = 1L;
UUID pathScheduleId = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe5");
FinishPreparationDto finishPreparationDto = FinishPreparationDto.builder()
.scheduleId(UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe6"))
.latenessTime(0)
.build();

when(userAuthService.getUserIdFromToken(any())).thenReturn(userId);
doThrow(new GeneralException(ErrorCode.SCHEDULE_ID_MISMATCH))
.when(scheduleService).finishSchedule(eq(userId), eq(pathScheduleId), any(FinishPreparationDto.class));

// when // then
mockMvc.perform(
put("/schedules/{scheduleId}/finish", pathScheduleId)
.content(objectMapper.writeValueAsString(finishPreparationDto))
.contentType(MediaType.APPLICATION_JSON)
)
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value(ErrorCode.SCHEDULE_ID_MISMATCH.getCode()))
.andExpect(jsonPath("$.status").value("error"))
.andExpect(jsonPath("$.message").value(ErrorCode.SCHEDULE_ID_MISMATCH.getMessage()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1233,16 +1233,17 @@ void updateLatenessTime(){
.build();

// when
scheduleService.updateLatenessTime(finishPreparationDto);
Schedule schedule = scheduleRepository.findById(finishPreparationDto.getScheduleId()).get();
scheduleService.updateLatenessTime(schedule, finishPreparationDto.getLatenessTime());

// then
assertThat(scheduleRepository.findById(UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe5"))
.get().getLatenessTime()).isEqualTo(1);
}

@DisplayName("지각 시간 업데이트할 때, 잘못된 schedulId를 DTO에 담아 요청하는 경우 예외가 발생한다.")
@DisplayName("약속 종료할 때, 경로와 본문의 scheduleId가 다르면 예외가 발생한다.")
@Test
void updateLatenessTimeWithWrongScheduleId(){
void finishScheduleWithScheduleIdMismatch(){
// given
User addedUser = User.builder()
.email("user@example.com")
Expand All @@ -1269,9 +1270,19 @@ void updateLatenessTimeWithWrongScheduleId(){
.build();

// when // then
assertThatThrownBy(() -> scheduleService.updateLatenessTime(finishPreparationDto))
assertThatThrownBy(() -> scheduleService.finishSchedule(
addedUser.getId(),
UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe5"),
finishPreparationDto
))
.isInstanceOf(GeneralException.class)
.hasMessage("해당 약속이 존재하지 않습니다.");
.hasMessage(ErrorCode.SCHEDULE_ID_MISMATCH.getMessage())
.extracting("errorCode")
.isEqualTo(ErrorCode.SCHEDULE_ID_MISMATCH);

assertThat(scheduleRepository.findById(UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe5"))
.get().getLatenessTime()).isEqualTo(-1);
assertThat(userRepository.findById(addedUser.getId()).get().getPunctualityScore()).isEqualTo(-1f);
}

@DisplayName("약속을 종료해 지각시간과 성실도점수 업데이트에 성공한다.")
Expand All @@ -1298,12 +1309,11 @@ void finishSchedule(){
scheduleRepository.save(addedSchedule);

FinishPreparationDto finishPreparationDto = FinishPreparationDto.builder()
.scheduleId(UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe5"))
.latenessTime(1)
.build();

// when
scheduleService.finishSchedule(addedUser.getId(), finishPreparationDto);
scheduleService.finishSchedule(addedUser.getId(), addedSchedule.getScheduleId(), finishPreparationDto);

// then
assertThat(scheduleRepository.findById(UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe5"))
Expand All @@ -1325,6 +1335,16 @@ void finishScheduleWithWrongUserId(){
.build();
userRepository.save(addedUser);

User otherUser = User.builder()
.email("other@example.com")
.password(passwordEncoder.encode("password1234"))
.name("other")
.punctualityScore(-1f)
.scheduleCountAfterReset(0)
.latenessCountAfterReset(0)
.build();
userRepository.save(otherUser);

Schedule addedSchedule = Schedule.builder()
.scheduleId(UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe5"))
.scheduleName("을사년 새해")
Expand All @@ -1340,9 +1360,15 @@ void finishScheduleWithWrongUserId(){
.build();

// when // then
assertThatThrownBy(() -> scheduleService.finishSchedule(addedUser.getId() + 1, finishPreparationDto))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("존재하지 않는 유저 id입니다.");
assertThatThrownBy(() -> scheduleService.finishSchedule(otherUser.getId(), addedSchedule.getScheduleId(), finishPreparationDto))
.isInstanceOf(GeneralException.class)
.hasMessage(ErrorCode.UNAUTHORIZED_ACCESS.getMessage())
.extracting("errorCode")
.isEqualTo(ErrorCode.UNAUTHORIZED_ACCESS);

assertThat(scheduleRepository.findById(addedSchedule.getScheduleId()).get().getLatenessTime()).isEqualTo(-1);
assertThat(userRepository.findById(addedUser.getId()).get().getPunctualityScore()).isEqualTo(-1f);
assertThat(userRepository.findById(otherUser.getId()).get().getPunctualityScore()).isEqualTo(-1f);
}

@DisplayName("약속을 종료할 때, 잘못된 scheduleId를 인자로 넘기는 경우 예외가 발생한다.")
Expand Down Expand Up @@ -1374,9 +1400,62 @@ void finishScheduleWithWrongScheduleId(){
.build();

// when // then
assertThatThrownBy(() -> scheduleService.finishSchedule(addedUser.getId(), finishPreparationDto))
assertThatThrownBy(() -> scheduleService.finishSchedule(
addedUser.getId(),
UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe6"),
finishPreparationDto
))
.isInstanceOf(GeneralException.class)
.hasMessage(ErrorCode.SCHEDULE_NOT_FOUND.getMessage())
.extracting("errorCode")
.isEqualTo(ErrorCode.SCHEDULE_NOT_FOUND);

assertThat(scheduleRepository.findById(addedSchedule.getScheduleId()).get().getLatenessTime()).isEqualTo(-1);
assertThat(userRepository.findById(addedUser.getId()).get().getPunctualityScore()).isEqualTo(-1f);
}

@DisplayName("이미 종료된 약속을 다시 종료하면 예외가 발생하고 성실도점수를 다시 계산하지 않는다.")
@Test
void finishScheduleWithAlreadyFinishedSchedule(){
// given
User addedUser = User.builder()
.email("user@example.com")
.password(passwordEncoder.encode("password1234"))
.name("junbeom")
.punctualityScore(100f)
.scheduleCountAfterReset(1)
.latenessCountAfterReset(0)
.build();
userRepository.save(addedUser);

Schedule addedSchedule = Schedule.builder()
.scheduleId(UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afe5"))
.scheduleName("을사년 새해")
.scheduleTime(LocalDateTime.of(2025, 1, 1, 0, 0))
.latenessTime(0)
.doneStatus(DoneStatus.NORMAL)
.user(addedUser)
.build();
scheduleRepository.save(addedSchedule);

FinishPreparationDto finishPreparationDto = FinishPreparationDto.builder()
.scheduleId(addedSchedule.getScheduleId())
.latenessTime(1)
.build();

// when // then
assertThatThrownBy(() -> scheduleService.finishSchedule(addedUser.getId(), addedSchedule.getScheduleId(), finishPreparationDto))
.isInstanceOf(GeneralException.class)
.hasMessage("해당 약속이 존재하지 않습니다.");
.hasMessage(ErrorCode.SCHEDULE_ALREADY_FINISHED.getMessage())
.extracting("errorCode")
.isEqualTo(ErrorCode.SCHEDULE_ALREADY_FINISHED);

User user = userRepository.findById(addedUser.getId()).get();
Schedule schedule = scheduleRepository.findById(addedSchedule.getScheduleId()).get();
assertThat(schedule.getLatenessTime()).isEqualTo(0);
assertThat(user.getPunctualityScore()).isEqualTo(100f);
assertThat(user.getScheduleCountAfterReset()).isEqualTo(1);
assertThat(user.getLatenessCountAfterReset()).isEqualTo(0);
}

@Test
Expand Down
Loading