Skip to content

Commit

Permalink
[BE] 약속 잠금 해제 기능 추가 (#127)
Browse files Browse the repository at this point in the history
* feat(MeetingService): 약속 잠금 해제 기능 및 검증 추가

* feat(MeetingController): 약속 잠금 해제 Controller 추가

* test(MeetingControllerTest): 공통된 토큰 생성 로직 메서드 분리

* refactor(Meeting): `this` 키워드 팀 컨벤션 적용

* refactor(MeetingService): `Meeting`, `Attendee` 존재하지 않을 경우 에러코드 변경
  • Loading branch information
ikjo39 authored Aug 1, 2024
1 parent 37e5ae2 commit 6440e0b
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ public MomoApiResponse<MeetingSharingResponse> findMeetingSharing(@PathVariable
public void lock(@PathVariable String uuid, @AuthAttendee long id) {
meetingService.lock(uuid, id);
}

@PatchMapping("/api/v1/meetings/{uuid}/unlock")
public void unlock(@PathVariable String uuid, @AuthAttendee long id) {
meetingService.unlock(uuid, id);
}
}
12 changes: 8 additions & 4 deletions backend/src/main/java/kr/momo/domain/meeting/Meeting.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,22 @@ public Meeting(String name, String uuid, LocalTime firstTime, LocalTime lastTime
}

public void lock() {
this.isLocked = true;
isLocked = true;
}

public void unlock() {
isLocked = false;
}

public Timeslot getValidatedTimeslot(LocalTime other) {
return this.timeslotInterval.getValidatedTimeslot(other);
return timeslotInterval.getValidatedTimeslot(other);
}

public LocalTime startTimeslotTime() {
return this.timeslotInterval.getStartTimeslot().getLocalTime();
return timeslotInterval.getStartTimeslot().getLocalTime();
}

public LocalTime endTimeslotTime() {
return this.timeslotInterval.getEndTimeslot().getLocalTime();
return timeslotInterval.getEndTimeslot().getLocalTime();
}
}
10 changes: 10 additions & 0 deletions backend/src/main/java/kr/momo/service/meeting/MeetingService.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,14 @@ private void validateHostPermission(Attendee attendee) {
throw new MomoException(AttendeeErrorCode.ACCESS_DENIED);
}
}

@Transactional
public void unlock(String uuid, long id) {
Meeting meeting = meetingRepository.findByUuid(uuid)
.orElseThrow(() -> new MomoException(MeetingErrorCode.INVALID_UUID));
Attendee attendee = attendeeRepository.findByIdAndMeeting(id, meeting)
.orElseThrow(() -> new MomoException(AttendeeErrorCode.INVALID_ATTENDEE));
validateHostPermission(attendee);
meeting.unlock();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,7 @@ void createByDuplicatedName() {
void lock() {
Meeting meeting = meetingRepository.save(MeetingFixture.DINNER.create());
Attendee attendee = attendeeRepository.save(AttendeeFixture.HOST_JAZZ.create(meeting));
AttendeeLoginRequest request = new AttendeeLoginRequest(attendee.name(), attendee.password());

String token = RestAssured.given().log().all()
.contentType(ContentType.JSON)
.body(request)
.when().post("/api/v1/meetings/{uuid}/login", meeting.getUuid())
.then().log().all()
.statusCode(HttpStatus.OK.value())
.extract().jsonPath().getString("data.token");
String token = getToken(attendee, meeting);

RestAssured.given().log().all()
.contentType(ContentType.JSON)
Expand All @@ -175,15 +167,7 @@ void lockWithInvalidUUID() {
String invalidUUID = "INVALID_UUID";
Meeting meeting = meetingRepository.save(MeetingFixture.DINNER.create());
Attendee attendee = attendeeRepository.save(AttendeeFixture.HOST_JAZZ.create(meeting));
AttendeeLoginRequest request = new AttendeeLoginRequest(attendee.name(), attendee.password());

String token = RestAssured.given().log().all()
.contentType(ContentType.JSON)
.body(request)
.when().post("/api/v1/meetings/{uuid}/login", meeting.getUuid())
.then().log().all()
.statusCode(HttpStatus.OK.value())
.extract().jsonPath().getString("data.token");
String token = getToken(attendee, meeting);

RestAssured.given().log().all()
.contentType(ContentType.JSON)
Expand All @@ -199,22 +183,75 @@ void lockWithInvalidUUID() {
void lockWithNoPermission() {
Meeting meeting = meetingRepository.save(MeetingFixture.DINNER.create());
Attendee attendee = attendeeRepository.save(AttendeeFixture.GUEST_PEDRO.create(meeting));
AttendeeLoginRequest request = new AttendeeLoginRequest(attendee.name(), attendee.password());
String token = getToken(attendee, meeting);

String token = RestAssured.given().log().all()
RestAssured.given().log().all()
.contentType(ContentType.JSON)
.body(request)
.when().post("/api/v1/meetings/{uuid}/login", meeting.getUuid())
.pathParam("uuid", meeting.getUuid())
.header("Authorization", "Bearer " + token)
.when().patch("/api/v1/meetings/{uuid}/lock")
.then().log().all()
.statusCode(HttpStatus.OK.value())
.extract().jsonPath().getString("data.token");
.statusCode(HttpStatus.FORBIDDEN.value());
}

@DisplayName("약속을 잠금을 해제하면 200 OK를 반환한다.")
@Test
void unlock() {
Meeting meeting = meetingRepository.save(MeetingFixture.DINNER.create());
Attendee attendee = attendeeRepository.save(AttendeeFixture.HOST_JAZZ.create(meeting));
String token = getToken(attendee, meeting);

RestAssured.given().log().all()
.contentType(ContentType.JSON)
.pathParam("uuid", meeting.getUuid())
.header("Authorization", "Bearer " + token)
.when().patch("/api/v1/meetings/{uuid}/lock")
.when().patch("/api/v1/meetings/{uuid}/unlock")
.then().log().all()
.statusCode(HttpStatus.OK.value());
}

@DisplayName("존재하지 않는 약속을 잠금 해제 시도하면 400 Bad Request를 반환한다.")
@Test
void unlockWithInvalidUUID() {
String invalidUUID = "INVALID_UUID";
Meeting meeting = meetingRepository.save(MeetingFixture.DINNER.create());
Attendee attendee = attendeeRepository.save(AttendeeFixture.HOST_JAZZ.create(meeting));
String token = getToken(attendee, meeting);

RestAssured.given().log().all()
.contentType(ContentType.JSON)
.pathParam("uuid", invalidUUID)
.header("Authorization", "Bearer " + token)
.when().patch("/api/v1/meetings/{uuid}/unlock")
.then().log().all()
.statusCode(HttpStatus.BAD_REQUEST.value());
}

@DisplayName("약속을 잠금을 해제할 때 호스트 권한이 없다면 403을 반환한다.")
@Test
void unlockWithNoPermission() {
Meeting meeting = meetingRepository.save(MeetingFixture.DINNER.create());
Attendee attendee = attendeeRepository.save(AttendeeFixture.GUEST_PEDRO.create(meeting));
String token = getToken(attendee, meeting);

RestAssured.given().log().all()
.contentType(ContentType.JSON)
.pathParam("uuid", meeting.getUuid())
.header("Authorization", "Bearer " + token)
.when().patch("/api/v1/meetings/{uuid}/unlock")
.then().log().all()
.statusCode(HttpStatus.FORBIDDEN.value());
}

private String getToken(Attendee attendee, Meeting meeting) {
AttendeeLoginRequest request = new AttendeeLoginRequest(attendee.name(), attendee.password());

return RestAssured.given().log().all()
.contentType(ContentType.JSON)
.body(request)
.when().post("/api/v1/meetings/{uuid}/login", meeting.getUuid())
.then().log().all()
.statusCode(HttpStatus.OK.value())
.extract().jsonPath().getString("data.token");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,54 @@ void throwsExceptionWhenAttendeeGuest() {
.isInstanceOf(MomoException.class)
.hasMessage(AttendeeErrorCode.ACCESS_DENIED.message());
}

@DisplayName("약속 잠금을 해제하면 잠금 상태가 변경된다.")
@Test
void unlock() {
Meeting meeting = meetingRepository.save(MeetingFixture.GAME.create());
Attendee attendee = attendeeRepository.save(AttendeeFixture.HOST_JAZZ.create(meeting));

meetingService.unlock(meeting.getUuid(), attendee.getId());
Meeting changedMeeting = meetingRepository.findById(meeting.getId()).orElseThrow();

assertThat(changedMeeting.isLocked()).isFalse();
}

@DisplayName("약속 잠금을 해제할 때 약속을 조회할 수 없다면 예외가 발생한다.")
@Test
void throwsExceptionWhenUnlockNoMeeting() {
Meeting meeting = meetingRepository.save(MeetingFixture.GAME.create());
Attendee attendee = attendeeRepository.save(AttendeeFixture.GUEST_PEDRO.create(meeting));
String uuid = "";
long id = attendee.getId();

assertThatThrownBy(() -> meetingService.unlock(uuid, id))
.isInstanceOf(MomoException.class)
.hasMessage(MeetingErrorCode.INVALID_UUID.message());
}

@DisplayName("약속 잠금을 해제할 때 참가자가 존재하지 않다면 예외가 발생한다.")
@Test
void throwsExceptionWhenUnlockNoAttendee() {
Meeting meeting = meetingRepository.save(MeetingFixture.GAME.create());
String uuid = meeting.getUuid();
long id = 1L;

assertThatThrownBy(() -> meetingService.unlock(uuid, id))
.isInstanceOf(MomoException.class)
.hasMessage(AttendeeErrorCode.INVALID_ATTENDEE.message());
}

@DisplayName("약속 잠금을 해제할 때 로그인된 참가자가 호스트가 아니면 예외가 발생한다.")
@Test
void throwsExceptionWhenUnlockAttendeeGuest() {
Meeting meeting = meetingRepository.save(MeetingFixture.GAME.create());
Attendee attendee = attendeeRepository.save(AttendeeFixture.GUEST_PEDRO.create(meeting));
String uuid = meeting.getUuid();
long id = attendee.getId();

assertThatThrownBy(() -> meetingService.unlock(uuid, id))
.isInstanceOf(MomoException.class)
.hasMessage(AttendeeErrorCode.ACCESS_DENIED.message());
}
}

0 comments on commit 6440e0b

Please sign in to comment.