From e384e45b63f5eab91bf67658f21cbb58c2069fd5 Mon Sep 17 00:00:00 2001 From: ddingmin Date: Sun, 7 Jan 2024 17:05:09 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=EA=B0=80=20?= =?UTF-8?q?=EC=B0=B8=EC=97=AC=ED=95=9C=20=EB=AA=A8=EC=9E=84=EC=97=90=20?= =?UTF-8?q?=EB=AA=A8=EC=9E=84=EC=9D=84=20=EC=B7=A8=EC=86=8C=ED=95=9C?= =?UTF-8?q?=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meeting/controller/MeetingController.java | 7 +++++++ .../net/teumteum/meeting/domain/Meeting.java | 5 ++++- .../teumteum/meeting/service/MeetingService.java | 16 ++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/teumteum/meeting/controller/MeetingController.java b/src/main/java/net/teumteum/meeting/controller/MeetingController.java index 6f3d88a5..a66a9ea1 100644 --- a/src/main/java/net/teumteum/meeting/controller/MeetingController.java +++ b/src/main/java/net/teumteum/meeting/controller/MeetingController.java @@ -46,6 +46,13 @@ public MeetingResponse addParticipant(@PathVariable("meetingId") Long meetingId) return meetingService.addParticipant(meetingId, userId); } + @DeleteMapping("/{meetingId}/participants") + @ResponseStatus(HttpStatus.OK) +public void deleteParticipant(@PathVariable("meetingId") Long meetingId) { + Long userId = securityService.getCurrentUserId(); + meetingService.cancelParticipant(meetingId, userId); + } + @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(IllegalArgumentException.class) public ErrorResponse handleIllegalArgumentException(IllegalArgumentException illegalArgumentException) { diff --git a/src/main/java/net/teumteum/meeting/domain/Meeting.java b/src/main/java/net/teumteum/meeting/domain/Meeting.java index 58f3309e..7c1a2c02 100644 --- a/src/main/java/net/teumteum/meeting/domain/Meeting.java +++ b/src/main/java/net/teumteum/meeting/domain/Meeting.java @@ -55,6 +55,10 @@ public void addParticipant(Long userId) { participantUserIds.add(userId); } + public void cancelParticipant(Long userId) { + participantUserIds.remove(userId); + } + public boolean alreadyParticipant(Long userId) { return participantUserIds.contains(userId); } @@ -85,5 +89,4 @@ private void assertTitle() { private void assertParticipantUserIds() { Assert.isTrue(participantUserIds.size() + 1 <= numberOfRecruits, "최대 참여자 수에 도달한 모임에 참여할 수 없습니다." + "[최대 참여자 수] : " + numberOfRecruits + "[현재 참여자 수] : " + participantUserIds.size()); } - } diff --git a/src/main/java/net/teumteum/meeting/service/MeetingService.java b/src/main/java/net/teumteum/meeting/service/MeetingService.java index 6396d295..ef211f3a 100644 --- a/src/main/java/net/teumteum/meeting/service/MeetingService.java +++ b/src/main/java/net/teumteum/meeting/service/MeetingService.java @@ -68,4 +68,20 @@ public MeetingResponse addParticipant(Long meetingId, Long userId) { existMeeting.addParticipant(userId); return MeetingResponse.of(existMeeting); } + + @Transactional + public void cancelParticipant(Long meetingId, Long userId) { + var existMeeting = meetingRepository.findById(meetingId) + .orElseThrow(() -> new IllegalArgumentException("meetingId에 해당하는 모임을 찾을 수 없습니다. \"" + meetingId + "\"")); + + if (!existMeeting.alreadyParticipant(userId)) { + throw new IllegalArgumentException("참여하지 않은 모임입니다."); + } + + if (!existMeeting.isOpen()) { + throw new IllegalArgumentException("종료된 모임에서 참여를 취소할 수 없습니다."); + } + + existMeeting.cancelParticipant(userId); + } } From 62b483e86b5d494eec5318de7d7af70c58af06de Mon Sep 17 00:00:00 2001 From: ddingmin Date: Sun, 7 Jan 2024 17:36:32 +0900 Subject: [PATCH 2/2] =?UTF-8?q?test:=20=EB=AA=A8=EC=9E=84=20=EC=B0=B8?= =?UTF-8?q?=EC=97=AC=20=EC=B7=A8=EC=86=8C=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 참여된 모임의 참여 취소를 성공한다. - 참여하지 않은 모임에 대한 예외 케이스 - 종료된 모임에 대한 예외 케이스 --- .../meeting/service/MeetingService.java | 8 +-- .../java/net/teumteum/integration/Api.java | 6 ++ .../integration/MeetingIntegrationTest.java | 60 +++++++++++++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/teumteum/meeting/service/MeetingService.java b/src/main/java/net/teumteum/meeting/service/MeetingService.java index ef211f3a..b124fe27 100644 --- a/src/main/java/net/teumteum/meeting/service/MeetingService.java +++ b/src/main/java/net/teumteum/meeting/service/MeetingService.java @@ -74,14 +74,14 @@ public void cancelParticipant(Long meetingId, Long userId) { var existMeeting = meetingRepository.findById(meetingId) .orElseThrow(() -> new IllegalArgumentException("meetingId에 해당하는 모임을 찾을 수 없습니다. \"" + meetingId + "\"")); - if (!existMeeting.alreadyParticipant(userId)) { - throw new IllegalArgumentException("참여하지 않은 모임입니다."); - } - if (!existMeeting.isOpen()) { throw new IllegalArgumentException("종료된 모임에서 참여를 취소할 수 없습니다."); } + if (!existMeeting.alreadyParticipant(userId)) { + throw new IllegalArgumentException("참여하지 않은 모임입니다."); + } + existMeeting.cancelParticipant(userId); } } diff --git a/src/test/java/net/teumteum/integration/Api.java b/src/test/java/net/teumteum/integration/Api.java index 423d0731..9baa7e0d 100644 --- a/src/test/java/net/teumteum/integration/Api.java +++ b/src/test/java/net/teumteum/integration/Api.java @@ -102,4 +102,10 @@ ResponseSpec joinMeeting(String token, Long meetingId) { .exchange(); } + ResponseSpec cancelMeeting(String token, Long meetingId) { + return webTestClient.delete() + .uri("/meetings/" + meetingId + "/participants") + .header(HttpHeaders.AUTHORIZATION, token) + .exchange(); + } } diff --git a/src/test/java/net/teumteum/integration/MeetingIntegrationTest.java b/src/test/java/net/teumteum/integration/MeetingIntegrationTest.java index 8e5c1418..20a6348a 100644 --- a/src/test/java/net/teumteum/integration/MeetingIntegrationTest.java +++ b/src/test/java/net/teumteum/integration/MeetingIntegrationTest.java @@ -252,4 +252,64 @@ void Return_400_bad_request_if_exceed_max_number_of_recruits_meeting_id_received .expectBody(ErrorResponse.class); } } + + @Nested + @DisplayName("미팅 참여 취소 API는") + class Cancel_meeting_api { + + @Test + @DisplayName("존재하는 모임의 id가 주어지면, 모임에 참여를 취소한다.") + void Cancel_meeting_if_exist_meeting_id_received() { + // given + var me = repository.saveAndGetUser(); + var meeting = repository.saveAndGetOpenMeeting(); + + loginContext.setUserId(me.getId()); + api.joinMeeting(VALID_TOKEN, meeting.getId()); + // when + var result = api.cancelMeeting(VALID_TOKEN, meeting.getId()); + // then + result.expectStatus().isOk(); + } + + @Test + @DisplayName("참여하지 않은 모임의 id가 주어지면, 400 Bad Request를 응답한다.") + void Return_400_bad_request_if_not_joined_meeting_id_received() { + // given + var me = repository.saveAndGetUser(); + var meeting = repository.saveAndGetOpenMeeting(); + + loginContext.setUserId(me.getId()); + // when + var result = api.cancelMeeting(VALID_TOKEN, meeting.getId()); + // then + Assertions.assertThat(result.expectStatus().isBadRequest() + .expectBody(ErrorResponse.class) + .returnResult() + .getResponseBody() + ) + .extracting(ErrorResponse::getMessage) + .isEqualTo("참여하지 않은 모임입니다."); + } + + @Test + @DisplayName("종료된 모임의 id가 주어진다면, 400 Bad Request를 응답한다.") + void Return_400_bad_request_if_closed_meeting_id_received() { + // given + var me = repository.saveAndGetUser(); + var meeting = repository.saveAndGetCloseMeeting(); + + loginContext.setUserId(me.getId()); + // when + var result = api.cancelMeeting(VALID_TOKEN, meeting.getId()); + // then + Assertions.assertThat(result.expectStatus().isBadRequest() + .expectBody(ErrorResponse.class) + .returnResult() + .getResponseBody() + ) + .extracting(ErrorResponse::getMessage) + .isEqualTo("종료된 모임에서 참여를 취소할 수 없습니다."); + } + } }