From d2e8bfc60afa4c38a7d82909f38837bf28fd9f69 Mon Sep 17 00:00:00 2001 From: jaeuk520 Date: Fri, 20 Sep 2024 11:59:48 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[#33]=20feat:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EC=9D=B4=EB=A0=A5=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C=20&=20=EB=AA=BD=EA=B3=A0DB=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 + .../covigator/controller/ChatController.java | 30 +++++++ .../java/com/ku/covigator/domain/Chat.java | 29 ++++++ .../dto/response/GetChatHistoryResponse.java | 26 ++++++ .../covigator/repository/ChatRepository.java | 11 +++ .../com/ku/covigator/service/ChatService.java | 25 ++++++ src/main/resources/application.yml | 4 + .../ku/covigator/service/ChatServiceTest.java | 89 +++++++++++++++++++ 8 files changed, 217 insertions(+) create mode 100644 src/main/java/com/ku/covigator/controller/ChatController.java create mode 100644 src/main/java/com/ku/covigator/domain/Chat.java create mode 100644 src/main/java/com/ku/covigator/dto/response/GetChatHistoryResponse.java create mode 100644 src/main/java/com/ku/covigator/repository/ChatRepository.java create mode 100644 src/main/java/com/ku/covigator/service/ChatService.java create mode 100644 src/test/java/com/ku/covigator/service/ChatServiceTest.java diff --git a/build.gradle b/build.gradle index 2a99175..c7e422f 100644 --- a/build.gradle +++ b/build.gradle @@ -93,6 +93,9 @@ dependencies { //websocket implementation 'org.springframework.boot:spring-boot-starter-websocket' + + // mongoDB + implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' } tasks.named('test') { diff --git a/src/main/java/com/ku/covigator/controller/ChatController.java b/src/main/java/com/ku/covigator/controller/ChatController.java new file mode 100644 index 0000000..5ef6394 --- /dev/null +++ b/src/main/java/com/ku/covigator/controller/ChatController.java @@ -0,0 +1,30 @@ +package com.ku.covigator.controller; + +import com.ku.covigator.domain.Chat; +import com.ku.covigator.dto.response.GetChatHistoryResponse; +import com.ku.covigator.service.ChatService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Tag(name = "Chat", description = "채팅") +@RestController +@RequiredArgsConstructor +public class ChatController { + + private final ChatService chatService; + + @Operation(summary = "채팅 기록 조회") + @GetMapping("/chat/{course_id}") + public ResponseEntity getChatHistory(@PathVariable(value = "course_id") Long courseId) { + List chatList = chatService.getChatHistory(courseId); + return ResponseEntity.ok(GetChatHistoryResponse.from(chatList)); + } + +} diff --git a/src/main/java/com/ku/covigator/domain/Chat.java b/src/main/java/com/ku/covigator/domain/Chat.java new file mode 100644 index 0000000..a9d0722 --- /dev/null +++ b/src/main/java/com/ku/covigator/domain/Chat.java @@ -0,0 +1,29 @@ +package com.ku.covigator.domain; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document(collection = "chat") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Chat { + + @Id + private String id; + private Long courseId; + private String timestamp; + private String nickname; + private String message; + + @Builder + public Chat(Long courseId, String timestamp, String nickname, String message) { + this.courseId = courseId; + this.timestamp = timestamp; + this.nickname = nickname; + this.message = message; + } +} diff --git a/src/main/java/com/ku/covigator/dto/response/GetChatHistoryResponse.java b/src/main/java/com/ku/covigator/dto/response/GetChatHistoryResponse.java new file mode 100644 index 0000000..485c458 --- /dev/null +++ b/src/main/java/com/ku/covigator/dto/response/GetChatHistoryResponse.java @@ -0,0 +1,26 @@ +package com.ku.covigator.dto.response; + +import com.ku.covigator.domain.Chat; +import lombok.Builder; + +import java.util.List; + +public record GetChatHistoryResponse(List chat) { + + @Builder + public record ChatDto(String nickname, String timestamp, String message) { + } + + public static GetChatHistoryResponse from(List chatList) { + + List chatDtos = chatList.stream() + .map(chat -> ChatDto.builder() + .nickname(chat.getNickname()) + .message(chat.getMessage()) + .timestamp(chat.getTimestamp()) + .build() + ).toList(); + + return new GetChatHistoryResponse(chatDtos); + } +} diff --git a/src/main/java/com/ku/covigator/repository/ChatRepository.java b/src/main/java/com/ku/covigator/repository/ChatRepository.java new file mode 100644 index 0000000..b554d16 --- /dev/null +++ b/src/main/java/com/ku/covigator/repository/ChatRepository.java @@ -0,0 +1,11 @@ +package com.ku.covigator.repository; + +import com.ku.covigator.domain.Chat; +import org.springframework.data.mongodb.repository.MongoRepository; + +import java.util.List; + +public interface ChatRepository extends MongoRepository { + + public List findChatByCourseIdOrderByTimestampAsc(Long courseId); +} diff --git a/src/main/java/com/ku/covigator/service/ChatService.java b/src/main/java/com/ku/covigator/service/ChatService.java new file mode 100644 index 0000000..12e4f1a --- /dev/null +++ b/src/main/java/com/ku/covigator/service/ChatService.java @@ -0,0 +1,25 @@ +package com.ku.covigator.service; + +import com.ku.covigator.domain.Chat; +import com.ku.covigator.exception.notfound.NotFoundCourseException; +import com.ku.covigator.repository.ChatRepository; +import com.ku.covigator.repository.CourseRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ChatService { + + private final ChatRepository chatRepository; + private final CourseRepository courseRepository; + + public List getChatHistory(Long courseId) { + + courseRepository.findById(courseId).orElseThrow(NotFoundCourseException::new); + return chatRepository.findChatByCourseIdOrderByTimestampAsc(courseId); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 73e0de7..d2611dd 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -18,6 +18,10 @@ spring: multipart: max-request-size: 5MB max-file-size: 10MB + data: + mongodb: + uri: ${MONGO_DB_URI} + database: ${MONGO_DB_DATABASE} security: jwt: diff --git a/src/test/java/com/ku/covigator/service/ChatServiceTest.java b/src/test/java/com/ku/covigator/service/ChatServiceTest.java new file mode 100644 index 0000000..747a567 --- /dev/null +++ b/src/test/java/com/ku/covigator/service/ChatServiceTest.java @@ -0,0 +1,89 @@ +package com.ku.covigator.service; + +import com.ku.covigator.domain.Chat; +import com.ku.covigator.domain.Course; +import com.ku.covigator.exception.notfound.NotFoundCourseException; +import com.ku.covigator.repository.ChatRepository; +import com.ku.covigator.repository.CourseRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.sql.Timestamp; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +class ChatServiceTest { + + @Autowired + private ChatService chatService; + @Autowired + private ChatRepository chatRepository; + @Autowired + private CourseRepository courseRepository; + + @AfterEach + void tearDown() { + courseRepository.deleteAllInBatch(); + chatRepository.deleteAll(); + } + + @DisplayName("채팅 기록을 오래된 순으로 조회한다.") + @Test + void getChatHistory() { + //given + Course course = Course.builder() + .name("건대 풀코스") + .isPublic('Y') + .description("건대 핫플 리스트") + .build(); + Course savedCourse = courseRepository.save(course); + + String time = new Timestamp(System.currentTimeMillis()).toString(); + Chat chat = Chat.builder() + .courseId(savedCourse.getId()) + .timestamp(time) + .nickname("김코비") + .message("여기 좋아요") + .build(); + + String time2 = new Timestamp(System.currentTimeMillis()).toString(); + Chat chat2 = Chat.builder() + .courseId(savedCourse.getId()) + .timestamp(time2) + .nickname("박코비") + .message("저는 별로,,") + .build(); + chatRepository.saveAll(List.of(chat, chat2)); + + //when + List chatList = chatService.getChatHistory(savedCourse.getId()); + + //then + assertAll( + () -> assertThat(chatList).hasSize(2), + () -> assertThat(chatList) + .extracting("nickname") + .containsExactly("김코비", "박코비"), + () -> assertThat(chatList) + .extracting("message") + .containsExactly("여기 좋아요", "저는 별로,,") + ); + } + + @DisplayName("존재하지 않는 코스에 대한 채팅 조회는 예외를 발생시킨다.") + @Test + void getChatHistoryFailsWhenNotFoundCourse() { + //given + + //when //then + assertThatThrownBy(() -> chatService.getChatHistory(100L)) + .isInstanceOf(NotFoundCourseException.class); + } + +} \ No newline at end of file From d1cf1c504b0b0c997f4b208ab7e688b33d331f2a Mon Sep 17 00:00:00 2001 From: jaeuk520 Date: Fri, 20 Sep 2024 12:00:51 +0900 Subject: [PATCH 2/5] =?UTF-8?q?chore:=20CI=20=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/backend-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index 7df1699..6fbad55 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -43,6 +43,8 @@ jobs: S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }} AWS_ACCESS_KEY: ${{ secrets.ACCESS_KEY_ID }} AWS_SECRET_KEY: ${{ secrets.ACCESS_KEY_SECRET }} + MONGO_DB_URI: ${{ secrets.MONGO_DB_URI }} + MONGO_DB_DATABASE: ${{ secrets.MONGO_DB_DATABASE }} with: arguments: check cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }} From 73ea4af5e12f2babde5fed9a1dcdf8d8f1abb6e2 Mon Sep 17 00:00:00 2001 From: jaeuk520 Date: Sun, 22 Sep 2024 21:03:10 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[#33]=20feat:=20=EC=9B=B9=EC=86=8C=EC=BC=93?= =?UTF-8?q?=20=EC=97=B0=EB=8F=99=20=EB=B0=8F=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ku/covigator/config/WebSocketConfig.java | 38 ++++++++++++ .../controller/WebSocketController.java | 28 +++++++++ .../dto/request/ChatMessageRequest.java | 5 ++ .../dto/response/SaveMessageResponse.java | 17 ++++++ .../security/jwt/StompJwtInterceptor.java | 40 ++++++++++++ .../com/ku/covigator/service/ChatService.java | 33 +++++++++- .../ku/covigator/service/ChatServiceTest.java | 61 +++++++++++++++++++ 7 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/ku/covigator/config/WebSocketConfig.java create mode 100644 src/main/java/com/ku/covigator/controller/WebSocketController.java create mode 100644 src/main/java/com/ku/covigator/dto/request/ChatMessageRequest.java create mode 100644 src/main/java/com/ku/covigator/dto/response/SaveMessageResponse.java create mode 100644 src/main/java/com/ku/covigator/security/jwt/StompJwtInterceptor.java diff --git a/src/main/java/com/ku/covigator/config/WebSocketConfig.java b/src/main/java/com/ku/covigator/config/WebSocketConfig.java new file mode 100644 index 0000000..29142be --- /dev/null +++ b/src/main/java/com/ku/covigator/config/WebSocketConfig.java @@ -0,0 +1,38 @@ +package com.ku.covigator.config; + +import com.ku.covigator.security.jwt.StompJwtInterceptor; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.ChannelRegistration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +@Configuration +@EnableWebSocketMessageBroker +@RequiredArgsConstructor +public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + + private final StompJwtInterceptor stompJwtInterceptor; + + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + + registry.addEndpoint("/ws-chat") + .setAllowedOriginPatterns("*") + .withSockJS(); + } + + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + + registry.enableSimpleBroker("/topic"); + registry.setApplicationDestinationPrefixes("/app"); + } + + @Override + public void configureClientInboundChannel(ChannelRegistration registration) { + registration.interceptors(stompJwtInterceptor); + } +} \ No newline at end of file diff --git a/src/main/java/com/ku/covigator/controller/WebSocketController.java b/src/main/java/com/ku/covigator/controller/WebSocketController.java new file mode 100644 index 0000000..55067d6 --- /dev/null +++ b/src/main/java/com/ku/covigator/controller/WebSocketController.java @@ -0,0 +1,28 @@ +package com.ku.covigator.controller; + +import com.ku.covigator.dto.request.ChatMessageRequest; +import com.ku.covigator.dto.response.SaveMessageResponse; +import com.ku.covigator.service.ChatService; +import lombok.RequiredArgsConstructor; +import org.springframework.messaging.handler.annotation.*; +import org.springframework.messaging.simp.SimpMessageHeaderAccessor; +import org.springframework.messaging.simp.SimpMessageSendingOperations; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class WebSocketController { + + private final SimpMessageSendingOperations simpleMessageSendingOperations; + private final ChatService chatService; + + @MessageMapping("/chat/{course_id}") + @SendTo("/topic/chat/{course_id}") + public void sendMessage(@DestinationVariable(value = "course_id") Long courseId, + SimpMessageHeaderAccessor accessor, + @Payload ChatMessageRequest request) { + Long memberId = Long.parseLong(accessor.getSessionAttributes().get("memberId").toString()); + SaveMessageResponse saveMessageResponse = chatService.saveMessage(memberId, courseId, request.message()); + simpleMessageSendingOperations.convertAndSend("/topic/chat/" + courseId, saveMessageResponse); + } +} diff --git a/src/main/java/com/ku/covigator/dto/request/ChatMessageRequest.java b/src/main/java/com/ku/covigator/dto/request/ChatMessageRequest.java new file mode 100644 index 0000000..e1d5ab6 --- /dev/null +++ b/src/main/java/com/ku/covigator/dto/request/ChatMessageRequest.java @@ -0,0 +1,5 @@ +package com.ku.covigator.dto.request; + +public record ChatMessageRequest(String message) { + +} diff --git a/src/main/java/com/ku/covigator/dto/response/SaveMessageResponse.java b/src/main/java/com/ku/covigator/dto/response/SaveMessageResponse.java new file mode 100644 index 0000000..1b5811e --- /dev/null +++ b/src/main/java/com/ku/covigator/dto/response/SaveMessageResponse.java @@ -0,0 +1,17 @@ +package com.ku.covigator.dto.response; + +import lombok.Builder; + +import java.sql.Timestamp; + +@Builder +public record SaveMessageResponse(String sender, String message, String timestamp) { + + public static SaveMessageResponse of(String sender, String message) { + return SaveMessageResponse.builder() + .sender(sender) + .message(message) + .timestamp(String.valueOf(new Timestamp(System.currentTimeMillis()))) + .build(); + } +} diff --git a/src/main/java/com/ku/covigator/security/jwt/StompJwtInterceptor.java b/src/main/java/com/ku/covigator/security/jwt/StompJwtInterceptor.java new file mode 100644 index 0000000..812b7af --- /dev/null +++ b/src/main/java/com/ku/covigator/security/jwt/StompJwtInterceptor.java @@ -0,0 +1,40 @@ +package com.ku.covigator.security.jwt; + +import com.ku.covigator.exception.jwt.JwtExpiredException; +import com.ku.covigator.exception.jwt.JwtInvalidException; +import com.ku.covigator.exception.jwt.JwtNotFoundException; +import com.ku.covigator.exception.jwt.JwtUnsupportedTokenException; +import lombok.RequiredArgsConstructor; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.stomp.StompCommand; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.messaging.support.ChannelInterceptor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class StompJwtInterceptor implements ChannelInterceptor { + + private final JwtProvider jwtProvider; + + @Override + public Message preSend(Message message, MessageChannel channel) { + + StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); + + if (accessor.getCommand() == StompCommand.CONNECT) { + String authorizationHeader = accessor.getFirstNativeHeader("Authorization"); + + String token = jwtProvider.getTokenFromRequestHeader(authorizationHeader); + + if(jwtProvider.validateToken(token)) { + String memberId = jwtProvider.getPrincipal(token); + accessor.getSessionAttributes().put("memberId", memberId); + } + } + + return message; + } + +} \ No newline at end of file diff --git a/src/main/java/com/ku/covigator/service/ChatService.java b/src/main/java/com/ku/covigator/service/ChatService.java index 12e4f1a..49a3e3f 100644 --- a/src/main/java/com/ku/covigator/service/ChatService.java +++ b/src/main/java/com/ku/covigator/service/ChatService.java @@ -1,12 +1,18 @@ package com.ku.covigator.service; import com.ku.covigator.domain.Chat; +import com.ku.covigator.domain.Course; +import com.ku.covigator.domain.member.Member; +import com.ku.covigator.dto.response.SaveMessageResponse; import com.ku.covigator.exception.notfound.NotFoundCourseException; +import com.ku.covigator.exception.notfound.NotFoundMemberException; import com.ku.covigator.repository.ChatRepository; import com.ku.covigator.repository.CourseRepository; +import com.ku.covigator.repository.MemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.sql.Timestamp; import java.util.List; @Service @@ -15,11 +21,36 @@ public class ChatService { private final ChatRepository chatRepository; private final CourseRepository courseRepository; - + private final MemberRepository memberRepository; + public List getChatHistory(Long courseId) { courseRepository.findById(courseId).orElseThrow(NotFoundCourseException::new); return chatRepository.findChatByCourseIdOrderByTimestampAsc(courseId); } + public SaveMessageResponse saveMessage(Long memberId, Long courseId, String message) { + + Member member = memberRepository.findById(memberId) + .orElseThrow(NotFoundMemberException::new); + + Course course = courseRepository.findById(courseId) + .orElseThrow(NotFoundCourseException::new); + + Chat chat = buildChat(course.getId(), message, member); + + chatRepository.save(chat); + + return SaveMessageResponse.of(member.getNickname(), message); + } + + private Chat buildChat(Long courseId, String message, Member member) { + return Chat.builder() + .message(message) + .nickname(member.getNickname()) + .timestamp(String.valueOf(new Timestamp(System.currentTimeMillis()))) + .courseId(courseId) + .build(); + } + } diff --git a/src/test/java/com/ku/covigator/service/ChatServiceTest.java b/src/test/java/com/ku/covigator/service/ChatServiceTest.java index 747a567..39137b6 100644 --- a/src/test/java/com/ku/covigator/service/ChatServiceTest.java +++ b/src/test/java/com/ku/covigator/service/ChatServiceTest.java @@ -2,10 +2,16 @@ import com.ku.covigator.domain.Chat; import com.ku.covigator.domain.Course; +import com.ku.covigator.domain.member.Member; +import com.ku.covigator.domain.member.Platform; +import com.ku.covigator.dto.response.SaveMessageResponse; import com.ku.covigator.exception.notfound.NotFoundCourseException; +import com.ku.covigator.exception.notfound.NotFoundMemberException; import com.ku.covigator.repository.ChatRepository; import com.ku.covigator.repository.CourseRepository; +import com.ku.covigator.repository.MemberRepository; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -26,11 +32,14 @@ class ChatServiceTest { private ChatRepository chatRepository; @Autowired private CourseRepository courseRepository; + @Autowired + private MemberRepository memberRepository; @AfterEach void tearDown() { courseRepository.deleteAllInBatch(); chatRepository.deleteAll(); + memberRepository.deleteAllInBatch(); } @DisplayName("채팅 기록을 오래된 순으로 조회한다.") @@ -86,4 +95,56 @@ void getChatHistoryFailsWhenNotFoundCourse() { .isInstanceOf(NotFoundCourseException.class); } + @DisplayName("채팅 메시지를 저장한다.") + @Test + void saveMessage() { + //given + Member member = Member.builder() + .platform(Platform.KAKAO) + .nickname("김코비") + .email("covi@naver.com") + .build(); + Member savedMember = memberRepository.save(member); + + Course course = Course.builder() + .name("건대 풀코스") + .isPublic('Y') + .description("건대 핫플 리스트") + .build(); + Course savedCourse = courseRepository.save(course); + + //when + SaveMessageResponse response = chatService.saveMessage(savedMember.getId(), savedCourse.getId(), "여기 좋아요"); + + //then + Assertions.assertAll( + () -> assertThat(response.message()).isEqualTo("여기 좋아요"), + () -> assertThat(response.sender()).isEqualTo("김코비") + ); + } + + @DisplayName("존재하지 않는 회원에 대한 채팅 메시지 저장은 예외를 발생시킨다.") + @Test + void saveMessageFailsWhenMemberNotFound() { + + //when //then + assertThatThrownBy(() -> chatService.saveMessage(100L, 100L, "")) + .isInstanceOf(NotFoundMemberException.class); + } + + @DisplayName("존재하지 않는 코스에 대한 채팅 메시지 저장은 예외를 발생시킨다.") + @Test + void saveMessageFailsWhenCourseNotFound() { + Member member = Member.builder() + .platform(Platform.KAKAO) + .nickname("김코비") + .email("covi@naver.com") + .build(); + Member savedMember = memberRepository.save(member); + + //when //then + assertThatThrownBy(() -> chatService.saveMessage(savedMember.getId(), 100L, "")) + .isInstanceOf(NotFoundCourseException.class); + } + } \ No newline at end of file From 16b89fea7fd8b27bc1d77231f27d7e7d05b73eac Mon Sep 17 00:00:00 2001 From: jaeuk520 Date: Sun, 22 Sep 2024 21:22:13 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[#33]=20test:=20ChatController=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ChatControllerTest.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/test/java/com/ku/covigator/controller/ChatControllerTest.java diff --git a/src/test/java/com/ku/covigator/controller/ChatControllerTest.java b/src/test/java/com/ku/covigator/controller/ChatControllerTest.java new file mode 100644 index 0000000..88d144e --- /dev/null +++ b/src/test/java/com/ku/covigator/controller/ChatControllerTest.java @@ -0,0 +1,66 @@ +package com.ku.covigator.controller; + +import com.ku.covigator.domain.Chat; +import com.ku.covigator.security.jwt.JwtAuthArgumentResolver; +import com.ku.covigator.security.jwt.JwtAuthInterceptor; +import com.ku.covigator.service.ChatService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.List; + +import static org.mockito.BDDMockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ComponentScan("com.ku.covigator.support") +@WebMvcTest(controllers = ChatController.class) +class ChatControllerTest { + + @Autowired + private MockMvc mockMvc; + @MockBean + private ChatService chatService; + @MockBean + private JwtAuthInterceptor jwtAuthInterceptor; + @MockBean + private JwtAuthArgumentResolver jwtAuthArgumentResolver; + + @DisplayName("채팅 기록 조회를 요청한다.") + @Test + void getChatHistory() throws Exception { + //given + Long courseId = 1L; + List chats = List.of( + Chat.builder() + .message("안녕") + .nickname("김코비") + .build(), + Chat.builder() + .message("안녕안녕") + .nickname("박코비") + .build() + ); + + given(chatService.getChatHistory(courseId)).willReturn(chats); + + //when //then + mockMvc.perform(get("/chat/{course_id}", courseId)) + .andDo(print()) + .andExpectAll( + status().isOk(), + jsonPath("$.chat[0].message").value("안녕"), + jsonPath("$.chat[1].message").value("안녕안녕"), + jsonPath("$.chat[0].nickname").value("김코비"), + jsonPath("$.chat[1].nickname").value("박코비") + ); + } + +} \ No newline at end of file From 7db05ef096dde1e91d3796ed37e658d5ed50cc9e Mon Sep 17 00:00:00 2001 From: jaeuk520 Date: Sun, 22 Sep 2024 21:29:27 +0900 Subject: [PATCH 5/5] [#33] fix: fixed code issues --- .../java/com/ku/covigator/controller/WebSocketController.java | 4 +++- src/main/java/com/ku/covigator/service/ChatService.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/ku/covigator/controller/WebSocketController.java b/src/main/java/com/ku/covigator/controller/WebSocketController.java index 55067d6..a195351 100644 --- a/src/main/java/com/ku/covigator/controller/WebSocketController.java +++ b/src/main/java/com/ku/covigator/controller/WebSocketController.java @@ -9,6 +9,8 @@ import org.springframework.messaging.simp.SimpMessageSendingOperations; import org.springframework.web.bind.annotation.RestController; +import java.util.Objects; + @RestController @RequiredArgsConstructor public class WebSocketController { @@ -21,7 +23,7 @@ public class WebSocketController { public void sendMessage(@DestinationVariable(value = "course_id") Long courseId, SimpMessageHeaderAccessor accessor, @Payload ChatMessageRequest request) { - Long memberId = Long.parseLong(accessor.getSessionAttributes().get("memberId").toString()); + Long memberId = Long.parseLong(Objects.requireNonNull(accessor.getSessionAttributes()).get("memberId").toString()); SaveMessageResponse saveMessageResponse = chatService.saveMessage(memberId, courseId, request.message()); simpleMessageSendingOperations.convertAndSend("/topic/chat/" + courseId, saveMessageResponse); } diff --git a/src/main/java/com/ku/covigator/service/ChatService.java b/src/main/java/com/ku/covigator/service/ChatService.java index 49a3e3f..f3e0ec4 100644 --- a/src/main/java/com/ku/covigator/service/ChatService.java +++ b/src/main/java/com/ku/covigator/service/ChatService.java @@ -25,8 +25,8 @@ public class ChatService { public List getChatHistory(Long courseId) { - courseRepository.findById(courseId).orElseThrow(NotFoundCourseException::new); - return chatRepository.findChatByCourseIdOrderByTimestampAsc(courseId); + Course course = courseRepository.findById(courseId).orElseThrow(NotFoundCourseException::new); + return chatRepository.findChatByCourseIdOrderByTimestampAsc(course.getId()); } public SaveMessageResponse saveMessage(Long memberId, Long courseId, String message) {