diff --git a/src/main/java/chainsawman/gesture/config/SecurityConfig.java b/src/main/java/chainsawman/gesture/config/SecurityConfig.java index 196a84a..df648b6 100644 --- a/src/main/java/chainsawman/gesture/config/SecurityConfig.java +++ b/src/main/java/chainsawman/gesture/config/SecurityConfig.java @@ -1,6 +1,5 @@ package chainsawman.gesture.config; -import chainsawman.gesture.global.TokenProvider; -import chainsawman.gesture.security.CustomUserDetailsService; + import chainsawman.gesture.security.InternalAuthFilter; import chainsawman.gesture.security.JwtAuthenticationFilter; import lombok.RequiredArgsConstructor; @@ -17,15 +16,13 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - @Configuration @EnableWebSecurity @EnableMethodSecurity @RequiredArgsConstructor public class SecurityConfig { - private final TokenProvider tokenProvider; - private final CustomUserDetailsService customUserDetailsService; + private final JwtAuthenticationFilter jwtAuthenticationFilter; private final InternalAuthFilter internalAuthFilter; @Bean @@ -38,7 +35,6 @@ public AuthenticationManager authenticationManager(AuthenticationConfiguration c return configuration.getAuthenticationManager(); } - @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http @@ -50,6 +46,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .authorizeHttpRequests(auth -> auth .requestMatchers( "/auth/**", + "/users/check-id", "/swagger-ui/**", "/swagger-ui.html", "/v3/api-docs/**", @@ -57,10 +54,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti ).permitAll() .anyRequest().permitAll() ) - .addFilterBefore(new JwtAuthenticationFilter(tokenProvider, customUserDetailsService), - UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .addFilterBefore(internalAuthFilter, JwtAuthenticationFilter.class); return http.build(); } - } diff --git a/src/main/java/chainsawman/gesture/exceptions/room/NotRoomHostException.java b/src/main/java/chainsawman/gesture/exceptions/room/NotRoomHostException.java index baa6912..98949a6 100644 --- a/src/main/java/chainsawman/gesture/exceptions/room/NotRoomHostException.java +++ b/src/main/java/chainsawman/gesture/exceptions/room/NotRoomHostException.java @@ -5,6 +5,6 @@ public class NotRoomHostException extends DomainException { public NotRoomHostException() { - super(HttpStatus.FORBIDDEN, "ROOM_007", "통화방 호스트만 초대할 수 있습니다."); + super(HttpStatus.FORBIDDEN, "ROOM_007", "통화방 호스트만 가능한 기능입니다."); } } diff --git a/src/main/java/chainsawman/gesture/exceptions/user/InvalidPasswordException.java b/src/main/java/chainsawman/gesture/exceptions/user/InvalidPasswordException.java index 0c5a1fa..02b3d67 100644 --- a/src/main/java/chainsawman/gesture/exceptions/user/InvalidPasswordException.java +++ b/src/main/java/chainsawman/gesture/exceptions/user/InvalidPasswordException.java @@ -5,6 +5,6 @@ public class InvalidPasswordException extends DomainException { public InvalidPasswordException() { - super(HttpStatus.BAD_REQUEST, "USER_003", "비밀번호가 올바르지 않습니다."); + super(HttpStatus.UNAUTHORIZED, "USER_003", "비밀번호가 올바르지 않습니다."); } } diff --git a/src/main/java/chainsawman/gesture/global/ApiGlobalResponseHandler.java b/src/main/java/chainsawman/gesture/global/ApiGlobalResponseHandler.java index e93ff60..44f135b 100644 --- a/src/main/java/chainsawman/gesture/global/ApiGlobalResponseHandler.java +++ b/src/main/java/chainsawman/gesture/global/ApiGlobalResponseHandler.java @@ -89,7 +89,7 @@ public ResponseEntity> handleValidException(MethodArgumentNotV .findFirst() .orElse("입력값이 올바르지 않습니다."); return ResponseEntity.status(HttpStatus.BAD_REQUEST) - .body(ApiResponse.error("COMMON_400", "입력값이 올바르지 않습니다.")); + .body(ApiResponse.error("COMMON_400", message)); } @ExceptionHandler(Exception.class) diff --git a/src/main/java/chainsawman/gesture/security/JwtAuthenticationFilter.java b/src/main/java/chainsawman/gesture/security/JwtAuthenticationFilter.java index 65cb78f..28068fe 100644 --- a/src/main/java/chainsawman/gesture/security/JwtAuthenticationFilter.java +++ b/src/main/java/chainsawman/gesture/security/JwtAuthenticationFilter.java @@ -1,6 +1,8 @@ package chainsawman.gesture.security; +import chainsawman.gesture.entity.user.User; import chainsawman.gesture.global.TokenProvider; +import chainsawman.gesture.repository.user.UserRepository; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -9,18 +11,19 @@ import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; +import java.util.List; +@Component @RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { private final TokenProvider tokenProvider; - private final UserDetailsService userDetailsService; + private final UserRepository userRepository; @Override protected void doFilterInternal( @@ -33,17 +36,13 @@ protected void doFilterInternal( if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) { String email = tokenProvider.getEmail(token); - UserDetails userDetails = userDetailsService.loadUserByUsername(email); - - if (SecurityContextHolder.getContext().getAuthentication() == null) { - UsernamePasswordAuthenticationToken authentication = - new UsernamePasswordAuthenticationToken( - userDetails, - null, - userDetails.getAuthorities() - ); - SecurityContextHolder.getContext().setAuthentication(authentication); - } + userRepository.findByEmailAndIsDeactivatedFalse(email).ifPresent(user -> { + if (SecurityContextHolder.getContext().getAuthentication() == null) { + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken(user, null, List.of()); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + }); } filterChain.doFilter(request, response); diff --git a/src/main/java/chainsawman/gesture/service/AuthService.java b/src/main/java/chainsawman/gesture/service/AuthService.java index d44dac0..6a5fc33 100644 --- a/src/main/java/chainsawman/gesture/service/AuthService.java +++ b/src/main/java/chainsawman/gesture/service/AuthService.java @@ -16,7 +16,7 @@ import chainsawman.gesture.global.TokenProvider; import chainsawman.gesture.repository.user.RefreshTokenRepository; import chainsawman.gesture.repository.user.UserRepository; -import jakarta.transaction.Transactional; +import org.springframework.transaction.annotation.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; diff --git a/src/main/java/chainsawman/gesture/service/CallService.java b/src/main/java/chainsawman/gesture/service/CallService.java index 8a71c06..db89c41 100644 --- a/src/main/java/chainsawman/gesture/service/CallService.java +++ b/src/main/java/chainsawman/gesture/service/CallService.java @@ -11,7 +11,7 @@ import chainsawman.gesture.exceptions.call.CallAlreadyJoinedException; import chainsawman.gesture.exceptions.call.CallParticipantNotFoundException; import chainsawman.gesture.exceptions.call.NoActiveCallException; -import chainsawman.gesture.exceptions.room.RoomMemberNotFoundException; +import chainsawman.gesture.exceptions.room.NotRoomMemberException; import chainsawman.gesture.exceptions.room.RoomNotFoundException; import chainsawman.gesture.repository.call.CallParticipantRepository; import chainsawman.gesture.repository.call.CallRepository; @@ -42,7 +42,7 @@ public CallJoinResponse joinCall(Long roomIdx) { .orElseThrow(RoomNotFoundException::new); if (!roomMemberRepository.existsByRoom_IdxAndUser_Idx(roomIdx, currentUser.getIdx())) { - throw new RoomMemberNotFoundException(); + throw new NotRoomMemberException(); } // 활성 통화 세션이 없으면 새로 생성 (최초 참여자가 호스트) diff --git a/src/main/java/chainsawman/gesture/service/MeetingService.java b/src/main/java/chainsawman/gesture/service/MeetingService.java index d2c4a57..3a7cbe6 100644 --- a/src/main/java/chainsawman/gesture/service/MeetingService.java +++ b/src/main/java/chainsawman/gesture/service/MeetingService.java @@ -21,7 +21,6 @@ import chainsawman.gesture.repository.room.RoomRepository; import chainsawman.gesture.security.SecurityUtils; import lombok.RequiredArgsConstructor; -import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/chainsawman/gesture/service/RoomService.java b/src/main/java/chainsawman/gesture/service/RoomService.java index 9a1abfd..22d3f4c 100644 --- a/src/main/java/chainsawman/gesture/service/RoomService.java +++ b/src/main/java/chainsawman/gesture/service/RoomService.java @@ -15,8 +15,8 @@ import chainsawman.gesture.exceptions.room.RoomFullException; import chainsawman.gesture.exceptions.room.RoomMaxParticipantExceededException; import chainsawman.gesture.exceptions.room.RoomMemberNotFoundException; +import chainsawman.gesture.exceptions.room.NotRoomHostException; import chainsawman.gesture.exceptions.room.RoomNotFoundException; -import chainsawman.gesture.dto.room.response.RoomLeaveResponse; import chainsawman.gesture.exceptions.user.InvalidPasswordException; import chainsawman.gesture.repository.chat.ChatParticipantRepository; import chainsawman.gesture.repository.chat.ChatRoomRepository; @@ -26,7 +26,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.security.access.AccessDeniedException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -160,7 +159,7 @@ public RoomPatchResponse patchRoom(Long roomIdx, RoomPatchRequest request) { .orElseThrow(RoomNotFoundException::new); if (!room.getHost().getIdx().equals(currentUser.getIdx())) { - throw new AccessDeniedException("방장만 수정할 수 있습니다."); + throw new NotRoomHostException(); } int currentCount = roomMemberRepository.countByRoom_Idx(roomIdx); @@ -208,7 +207,7 @@ public RoomDeleteResponse deleteRoom(Long roomIdx) { .orElseThrow(RoomNotFoundException::new); if (!room.getHost().getIdx().equals(currentUser.getIdx())) { - throw new AccessDeniedException("방장만 삭제할 수 있습니다."); + throw new NotRoomHostException(); } roomMemberRepository.deleteAllByRoom_Idx(roomIdx); diff --git a/src/main/java/chainsawman/gesture/service/UserService.java b/src/main/java/chainsawman/gesture/service/UserService.java index 4acc3cc..278a87b 100644 --- a/src/main/java/chainsawman/gesture/service/UserService.java +++ b/src/main/java/chainsawman/gesture/service/UserService.java @@ -15,7 +15,6 @@ import chainsawman.gesture.repository.user.UserRepository; import chainsawman.gesture.security.SecurityUtils; import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; diff --git a/src/test/java/chainsawman/gesture/service/CallServiceTest.java b/src/test/java/chainsawman/gesture/service/CallServiceTest.java index 13f8940..3df6432 100644 --- a/src/test/java/chainsawman/gesture/service/CallServiceTest.java +++ b/src/test/java/chainsawman/gesture/service/CallServiceTest.java @@ -10,7 +10,7 @@ import chainsawman.gesture.exceptions.call.CallAlreadyJoinedException; import chainsawman.gesture.exceptions.call.CallParticipantNotFoundException; import chainsawman.gesture.exceptions.call.NoActiveCallException; -import chainsawman.gesture.exceptions.room.RoomMemberNotFoundException; +import chainsawman.gesture.exceptions.room.NotRoomMemberException; import chainsawman.gesture.exceptions.room.RoomNotFoundException; import chainsawman.gesture.repository.call.CallParticipantRepository; import chainsawman.gesture.repository.call.CallRepository; @@ -73,7 +73,7 @@ void joinCall_creates_new_session_when_no_active_call() { given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(10L, 1L)).willReturn(true); given(callRepository.findByRoom_IdxAndEndedAtIsNull(10L)).willReturn(Optional.empty()); given(callRepository.save(any(Call.class))).willReturn(newCall); - given(callParticipantRepository.existsByCall_IdxAndUser_IdxAndLeftAtIsNull(100L, 1L)).willReturn(false); + given(callParticipantRepository.existsByUser_IdxAndLeftAtIsNull(1L)).willReturn(false); given(callParticipantRepository.save(any(CallParticipant.class))).willAnswer(inv -> { CallParticipant p = inv.getArgument(0); ReflectionTestUtils.setField(p, "joinedAt", LocalDateTime.now()); @@ -100,7 +100,7 @@ void joinCall_joins_existing_session() { given(roomRepository.findById(10L)).willReturn(Optional.of(room)); given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(10L, 1L)).willReturn(true); given(callRepository.findByRoom_IdxAndEndedAtIsNull(10L)).willReturn(Optional.of(existingCall)); - given(callParticipantRepository.existsByCall_IdxAndUser_IdxAndLeftAtIsNull(100L, 1L)).willReturn(false); + given(callParticipantRepository.existsByUser_IdxAndLeftAtIsNull(1L)).willReturn(false); given(callParticipantRepository.save(any(CallParticipant.class))).willAnswer(inv -> { CallParticipant p = inv.getArgument(0); ReflectionTestUtils.setField(p, "joinedAt", LocalDateTime.now()); @@ -127,14 +127,14 @@ void joinCall_room_not_found() { } @Test - @DisplayName("통화 참여 - 방 멤버가 아니면 RoomMemberNotFoundException") + @DisplayName("통화 참여 - 방 멤버가 아니면 NotRoomMemberException") void joinCall_not_a_room_member() { given(securityUtils.getCurrentUser()).willReturn(user); given(roomRepository.findById(10L)).willReturn(Optional.of(room)); given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(10L, 1L)).willReturn(false); assertThatThrownBy(() -> callService.joinCall(10L)) - .isInstanceOf(RoomMemberNotFoundException.class); + .isInstanceOf(NotRoomMemberException.class); } @Test @@ -146,7 +146,7 @@ void joinCall_already_joined() { given(roomRepository.findById(10L)).willReturn(Optional.of(room)); given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(10L, 1L)).willReturn(true); given(callRepository.findByRoom_IdxAndEndedAtIsNull(10L)).willReturn(Optional.of(existingCall)); - given(callParticipantRepository.existsByCall_IdxAndUser_IdxAndLeftAtIsNull(100L, 1L)).willReturn(true); + given(callParticipantRepository.existsByUser_IdxAndLeftAtIsNull(1L)).willReturn(true); assertThatThrownBy(() -> callService.joinCall(10L)) .isInstanceOf(CallAlreadyJoinedException.class); diff --git a/src/test/java/chainsawman/gesture/service/FriendServiceTest.java b/src/test/java/chainsawman/gesture/service/FriendServiceTest.java index d288d34..ad4be05 100644 --- a/src/test/java/chainsawman/gesture/service/FriendServiceTest.java +++ b/src/test/java/chainsawman/gesture/service/FriendServiceTest.java @@ -18,6 +18,7 @@ import chainsawman.gesture.exceptions.user.UserNotFoundException; import chainsawman.gesture.repository.friend.FriendInviteRepository; import chainsawman.gesture.repository.friend.FriendshipRepository; +import chainsawman.gesture.repository.room.RoomMemberRepository; import chainsawman.gesture.repository.room.RoomRepository; import chainsawman.gesture.repository.user.UserRepository; import chainsawman.gesture.security.SecurityUtils; @@ -47,6 +48,7 @@ class FriendServiceTest { @Mock FriendInviteRepository friendInviteRepository; @Mock UserRepository userRepository; @Mock RoomRepository roomRepository; + @Mock RoomMemberRepository roomMemberRepository; @Mock SecurityUtils securityUtils; @InjectMocks FriendService friendService; @@ -81,6 +83,7 @@ void invite_success() { given(securityUtils.getCurrentUser()).willReturn(sender); given(userRepository.findByIdxAndIsDeactivatedFalse(2L)).willReturn(Optional.of(receiver)); given(roomRepository.findById(55L)).willReturn(Optional.of(room)); + given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(55L, 2L)).willReturn(false); given(friendInviteRepository.save(any(FriendInvite.class))).willAnswer(inv -> { FriendInvite invite = inv.getArgument(0); ReflectionTestUtils.setField(invite, "idx", 10L); @@ -132,6 +135,7 @@ void invite_duplicate_pending() { given(securityUtils.getCurrentUser()).willReturn(sender); given(userRepository.findByIdxAndIsDeactivatedFalse(2L)).willReturn(Optional.of(receiver)); given(roomRepository.findById(55L)).willReturn(Optional.of(room)); + given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(55L, 2L)).willReturn(false); given(friendInviteRepository.existsBySender_IdxAndReceiver_IdxAndRoom_IdxAndStatus( 1L, 2L, 55L, InvitationStatus.PENDING)).willReturn(true); diff --git a/src/test/java/chainsawman/gesture/service/MeetingServiceTest.java b/src/test/java/chainsawman/gesture/service/MeetingServiceTest.java index e6b3e29..9e7b507 100644 --- a/src/test/java/chainsawman/gesture/service/MeetingServiceTest.java +++ b/src/test/java/chainsawman/gesture/service/MeetingServiceTest.java @@ -12,9 +12,11 @@ import chainsawman.gesture.exceptions.meeting.MeetingAlreadyStartedException; import chainsawman.gesture.exceptions.meeting.MeetingNotFoundException; import chainsawman.gesture.exceptions.meeting.MeetingNotInProgressException; +import chainsawman.gesture.exceptions.room.NotRoomMemberException; import chainsawman.gesture.exceptions.room.RoomNotFoundException; import chainsawman.gesture.repository.call.CallRepository; import chainsawman.gesture.repository.meeting.MeetingRepository; +import chainsawman.gesture.repository.room.RoomMemberRepository; import chainsawman.gesture.repository.room.RoomRepository; import chainsawman.gesture.security.SecurityUtils; import org.junit.jupiter.api.BeforeEach; @@ -26,7 +28,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; -import org.springframework.security.access.AccessDeniedException; import org.springframework.test.util.ReflectionTestUtils; import java.time.LocalDateTime; @@ -48,6 +49,7 @@ class MeetingServiceTest { @Mock MeetingRepository meetingRepository; @Mock CallRepository callRepository; @Mock RoomRepository roomRepository; + @Mock RoomMemberRepository roomMemberRepository; @Mock SecurityUtils securityUtils; @InjectMocks MeetingService meetingService; @@ -194,12 +196,10 @@ void endMinutes_success() { meeting.setAiSummary("프로젝트 일정 논의"); meeting.setConclusion(List.of("개발 완료 목표 6월")); - given(callRepository.findById(100L)).willReturn(Optional.of(call)); - given(meetingRepository.findByCall_IdxAndStatus(100L, MeetingStatus.IN_PROGRESS)) - .willReturn(Optional.of(meeting)); + given(meetingRepository.findById(1L)).willReturn(Optional.of(meeting)); given(meetingRepository.save(any())).willAnswer(inv -> inv.getArgument(0)); - MeetingEndResponse response = meetingService.endMinutes(100L); + MeetingEndResponse response = meetingService.endMinutes(1L); assertThat(meeting.getStatus()).isEqualTo(MeetingStatus.ENDED); assertThat(meeting.getEndedAt()).isNotNull(); @@ -209,23 +209,23 @@ void endMinutes_success() { } @Test - @DisplayName("회의록 종료 - 진행 중인 회의록 없으면 MeetingNotInProgressException") + @DisplayName("회의록 종료 - ENDED 상태인 회의록이면 MeetingNotInProgressException") void endMinutes_not_in_progress() { - given(callRepository.findById(100L)).willReturn(Optional.of(call)); - given(meetingRepository.findByCall_IdxAndStatus(100L, MeetingStatus.IN_PROGRESS)) - .willReturn(Optional.empty()); + Meeting endedMeeting = buildEndedMeeting(1L, "이미 종료된 회의"); - assertThatThrownBy(() -> meetingService.endMinutes(100L)) + given(meetingRepository.findById(1L)).willReturn(Optional.of(endedMeeting)); + + assertThatThrownBy(() -> meetingService.endMinutes(1L)) .isInstanceOf(MeetingNotInProgressException.class); } @Test - @DisplayName("회의록 종료 - 존재하지 않는 통화 세션이면 CallNotFoundException") - void endMinutes_call_not_found() { - given(callRepository.findById(999L)).willReturn(Optional.empty()); + @DisplayName("회의록 종료 - 존재하지 않는 회의록이면 MeetingNotFoundException") + void endMinutes_minutes_not_found() { + given(meetingRepository.findById(999L)).willReturn(Optional.empty()); assertThatThrownBy(() -> meetingService.endMinutes(999L)) - .isInstanceOf(CallNotFoundException.class); + .isInstanceOf(MeetingNotFoundException.class); } // ─── getMinutesList ─────────────────────────── @@ -277,6 +277,7 @@ void getMinutesDetail_success() { meeting.setConclusion(List.of("결론 1", "결론 2")); given(meetingRepository.findById(1L)).willReturn(Optional.of(meeting)); + given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(10L, 1L)).willReturn(true); MeetingDetailResponse response = meetingService.getMinutesDetail(1L); @@ -305,9 +306,9 @@ void updateMinutes_success() { Meeting meeting = buildEndedMeeting(1L, "원래 제목"); meeting.setContent("원래 내용"); meeting.setConclusion(new ArrayList<>(List.of("원래 결론"))); - ReflectionTestUtils.setField(meeting, "createdBy", user); given(meetingRepository.findById(1L)).willReturn(Optional.of(meeting)); + given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(10L, 1L)).willReturn(true); given(meetingRepository.save(any())).willAnswer(inv -> inv.getArgument(0)); MeetingDetailResponse response = meetingService.updateMinutes(1L, @@ -323,9 +324,9 @@ void updateMinutes_success() { void updateMinutes_null_fields_preserved() { Meeting meeting = buildEndedMeeting(1L, "원래 제목"); meeting.setContent("원래 내용"); - ReflectionTestUtils.setField(meeting, "createdBy", user); given(meetingRepository.findById(1L)).willReturn(Optional.of(meeting)); + given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(10L, 1L)).willReturn(true); given(meetingRepository.save(any())).willAnswer(inv -> inv.getArgument(0)); MeetingDetailResponse response = meetingService.updateMinutes(1L, buildUpdateRequest(null, null, null)); @@ -335,18 +336,15 @@ void updateMinutes_null_fields_preserved() { } @Test - @DisplayName("회의록 수정 - 본인이 시작하지 않은 회의록이면 AccessDeniedException") + @DisplayName("회의록 수정 - 방 멤버가 아니면 NotRoomMemberException") void updateMinutes_access_denied() { - User otherUser = new User(); - ReflectionTestUtils.setField(otherUser, "idx", 2L); - Meeting meeting = buildEndedMeeting(1L, "회의"); - ReflectionTestUtils.setField(meeting, "createdBy", otherUser); given(meetingRepository.findById(1L)).willReturn(Optional.of(meeting)); + given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(10L, 1L)).willReturn(false); assertThatThrownBy(() -> meetingService.updateMinutes(1L, buildUpdateRequest("제목", null, null))) - .isInstanceOf(AccessDeniedException.class); + .isInstanceOf(NotRoomMemberException.class); verify(meetingRepository, never()).save(any()); } @@ -366,9 +364,9 @@ void updateMinutes_not_found() { @DisplayName("회의록 삭제 - 정상 삭제") void deleteMinutes_success() { Meeting meeting = buildEndedMeeting(1L, "회의"); - ReflectionTestUtils.setField(meeting, "createdBy", user); given(meetingRepository.findById(1L)).willReturn(Optional.of(meeting)); + given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(10L, 1L)).willReturn(true); meetingService.deleteMinutes(1L); @@ -376,18 +374,15 @@ void deleteMinutes_success() { } @Test - @DisplayName("회의록 삭제 - 본인이 시작하지 않은 회의록이면 AccessDeniedException") + @DisplayName("회의록 삭제 - 방 멤버가 아니면 NotRoomMemberException") void deleteMinutes_access_denied() { - User otherUser = new User(); - ReflectionTestUtils.setField(otherUser, "idx", 2L); - Meeting meeting = buildEndedMeeting(1L, "회의"); - ReflectionTestUtils.setField(meeting, "createdBy", otherUser); given(meetingRepository.findById(1L)).willReturn(Optional.of(meeting)); + given(roomMemberRepository.existsByRoom_IdxAndUser_Idx(10L, 1L)).willReturn(false); assertThatThrownBy(() -> meetingService.deleteMinutes(1L)) - .isInstanceOf(AccessDeniedException.class); + .isInstanceOf(NotRoomMemberException.class); verify(meetingRepository, never()).delete(any()); } @@ -408,6 +403,7 @@ void deleteMinutes_not_found() { private Meeting buildInProgressMeeting(Long idx) { Meeting meeting = Meeting.builder() .call(call) + .room(room) .createdBy(user) .status(MeetingStatus.IN_PROGRESS) .startedAt(LocalDateTime.now()) @@ -419,6 +415,7 @@ private Meeting buildInProgressMeeting(Long idx) { private Meeting buildEndedMeeting(Long idx, String title) { Meeting meeting = Meeting.builder() .call(call) + .room(room) .createdBy(user) .title(title) .status(MeetingStatus.ENDED)