-
-
+
+
+
+
+
@@ -46,47 +49,66 @@
WebSocket Chat Test Client
diff --git a/src/test/java/com/chat/yourway/integration/controller/ChatControllerTest.java b/src/test/java/com/chat/yourway/integration/controller/ChatControllerTest.java
new file mode 100644
index 00000000..ad593c6b
--- /dev/null
+++ b/src/test/java/com/chat/yourway/integration/controller/ChatControllerTest.java
@@ -0,0 +1,218 @@
+package com.chat.yourway.integration.controller;
+
+import static com.chat.yourway.model.Role.USER;
+import static com.chat.yourway.model.token.TokenType.BEARER;
+import static com.github.springtestdbunit.annotation.DatabaseOperation.CLEAN_INSERT;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.chat.yourway.dto.request.MessagePrivateRequestDto;
+import com.chat.yourway.dto.request.MessagePublicRequestDto;
+import com.chat.yourway.dto.response.MessageResponseDto;
+import com.chat.yourway.integration.controller.websocketclient.TestStompFrameHandler;
+import com.chat.yourway.integration.extension.PostgresExtension;
+import com.chat.yourway.integration.extension.RedisExtension;
+import com.chat.yourway.model.Contact;
+import com.chat.yourway.model.token.Token;
+import com.chat.yourway.repository.TokenRedisRepository;
+import com.chat.yourway.security.JwtService;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.springtestdbunit.DbUnitTestExecutionListener;
+import com.github.springtestdbunit.annotation.DatabaseSetup;
+import java.util.concurrent.CompletableFuture;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener;
+import org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.messaging.simp.stomp.StompHeaders;
+import org.springframework.messaging.simp.stomp.StompSession;
+import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
+import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
+import org.springframework.web.socket.WebSocketHttpHeaders;
+import org.springframework.web.socket.client.standard.StandardWebSocketClient;
+import org.springframework.web.socket.messaging.WebSocketStompClient;
+
+@Slf4j
+@ExtendWith({PostgresExtension.class, RedisExtension.class})
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@TestExecutionListeners({
+ DependencyInjectionTestExecutionListener.class,
+ DbUnitTestExecutionListener.class,
+ MockitoTestExecutionListener.class,
+ ResetMocksTestExecutionListener.class
+})
+@DatabaseSetup(value = {
+ "/dataset/mockdb/topic.xml",
+ "/dataset/mockdb/contact.xml",
+ "/dataset/mockdb/topic_subscriber.xml",
+ "/dataset/mockdb/message.xml"
+}, type = CLEAN_INSERT)
+public class ChatControllerTest {
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @Autowired
+ private TokenRedisRepository tokenRedisRepository;
+
+ @Autowired
+ private JwtService jwtService;
+
+ @LocalServerPort
+ private int port;
+ private StompSession session;
+
+ @BeforeEach
+ void setUp() {
+ String accessToken = getAccessToken();
+
+ String URL = "ws://localhost:" + port + "/chat";
+
+ WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
+ headers.add("Authorization", "Bearer " + accessToken);
+
+ session = createWebSocketSession(URL, headers);
+ }
+
+ @AfterEach
+ public void cleanup() {
+ session.disconnect();
+ }
+
+
+ @Test
+ @SneakyThrows
+ @DisplayName("sendToTopic should return correct send to public topic and received message from topic")
+ void sendToTopic_shouldReturnCorrectSendToPublicTopicAndReceivedMessageFromTopic() {
+ // Given
+ int topicId = 13;
+ MessagePublicRequestDto messageRequestDto = new MessagePublicRequestDto("Hello");
+ //Stored subscription results for testing
+ CompletableFuture
resultKeeper = new CompletableFuture<>();
+
+ // Subscribe to topic
+ session.subscribe("/topic/" + topicId,
+ new TestStompFrameHandler<>(resultKeeper, objectMapper, MessageResponseDto.class));
+
+ // Send message to topic
+ byte[] messageBytes = objectMapper.writeValueAsBytes(messageRequestDto);
+ session.send("/app/topic/" + topicId, messageBytes);
+
+ // Then
+ MessageResponseDto messageResponseDto = resultKeeper.get(3, SECONDS);
+ assertThat(messageResponseDto).isNotNull();
+ assertThat(messageResponseDto.getId()).isNotNull();
+ assertThat(messageResponseDto.getSentFrom()).isEqualTo("vasil@gmail.com");
+ assertThat(messageResponseDto.getSendTo()).isEqualTo("Topic id=" + topicId);
+ assertThat(messageResponseDto.getTimestamp()).isNotNull();
+ assertThat(messageResponseDto.getContent()).isEqualTo(messageRequestDto.getContent());
+ }
+
+ @Test
+ @SneakyThrows
+ @DisplayName("sendToContact should return correct received private message from self to self")
+ void sendToContact_shouldReturnCorrectReceivedPrivateMessageFromSelfToSelf() {
+ // Given
+ MessagePrivateRequestDto messageRequestDto = new MessagePrivateRequestDto();
+ messageRequestDto.setSendTo("vasil@gmail.com");
+ messageRequestDto.setContent("Hi Vasil!");
+ //Stored subscription results for testing
+ CompletableFuture resultKeeper = new CompletableFuture<>();
+
+ // Subscribe to private contact
+ session.subscribe("/user/specific",
+ new TestStompFrameHandler<>(resultKeeper, objectMapper, MessageResponseDto.class));
+
+ // Send private message to contact
+ byte[] messageBytes = objectMapper.writeValueAsBytes(messageRequestDto);
+ session.send("/app/private", messageBytes);
+
+ // Then
+ MessageResponseDto messageResponseDto = resultKeeper.get(3, SECONDS);
+ assertThat(messageResponseDto).isNotNull();
+ assertThat(messageResponseDto.getId()).isNotNull();
+ assertThat(messageResponseDto.getSentFrom()).isEqualTo("vasil@gmail.com");
+ assertThat(messageResponseDto.getSendTo()).isEqualTo("vasil@gmail.com");
+ assertThat(messageResponseDto.getTimestamp()).isNotNull();
+ assertThat(messageResponseDto.getContent()).isEqualTo(messageRequestDto.getContent());
+ }
+
+ @Test
+ @SneakyThrows
+ @DisplayName("getMessages should return received messages history from topic")
+ void getMessages_shouldReturnReceivedMessagesHistoryFromTopic() {
+ // Given
+ int topicId = 12;
+ //Stored subscription results for testing
+ CompletableFuture resultKeeper = new CompletableFuture<>();
+
+ // Subscribe to topic
+ session.subscribe("/topic/" + topicId,
+ new TestStompFrameHandler<>(resultKeeper, objectMapper, MessageResponseDto[].class));
+
+ // Send to topic
+ session.send("/app/get/messages/" + topicId, new byte[0]);
+
+ // Then
+ MessageResponseDto[] messageResponseDtos = resultKeeper.get(3, SECONDS);
+ assertThat(messageResponseDtos).isNotNull();
+ assertThat(messageResponseDtos).extracting("id").isNotNull();
+ assertThat(messageResponseDtos).extracting("timestamp").isNotNull();
+ assertThat(messageResponseDtos).extracting("sentFrom")
+ .contains("anton@gmail.com", "vasil@gmail.com");
+ assertThat(messageResponseDtos).extracting("sendTo")
+ .contains("vasil@gmail.com", "anton@gmail.com");
+ assertThat(messageResponseDtos).extracting("content")
+ .contains("hello Vasil!", "hello Anton!");
+ }
+
+ //-----------------------------------
+ // Private methods
+ //-----------------------------------
+
+ private String getAccessToken() {
+ String accessToken = jwtService.generateAccessToken(Contact.builder()
+ .email("vasil@gmail.com")
+ .password("Password-123")
+ .role(USER)
+ .build());
+
+ saveTokenToRedis(accessToken);
+
+ return accessToken;
+ }
+
+ private void saveTokenToRedis(String accessToken) {
+ tokenRedisRepository.save(Token.builder()
+ .email("vasil@gmail.com")
+ .token(accessToken)
+ .tokenType(BEARER)
+ .expired(false)
+ .revoked(false)
+ .build());
+ }
+
+ private StompSession createWebSocketSession(String URL, WebSocketHttpHeaders headers) {
+ WebSocketStompClient stompClient = new WebSocketStompClient(new StandardWebSocketClient());
+ var connectSession = stompClient.connectAsync(URL, headers, new StompSessionHandlerAdapter() {
+ @Override
+ public void afterConnected(@NotNull StompSession session,
+ @NotNull StompHeaders connectedHeaders) {
+ log.info("Test session was successfully connected {}", session.getSessionId());
+ }
+ });
+ // Wait for the connection to be established
+ return connectSession.join();
+ }
+
+}
diff --git a/src/test/java/com/chat/yourway/integration/controller/MessageControllerTest.java b/src/test/java/com/chat/yourway/integration/controller/MessageControllerTest.java
index 421560a3..1aed810a 100644
--- a/src/test/java/com/chat/yourway/integration/controller/MessageControllerTest.java
+++ b/src/test/java/com/chat/yourway/integration/controller/MessageControllerTest.java
@@ -77,7 +77,7 @@ void findAllByTopicId_shouldReturnEmptyListOfAllMessagesByTopicId() throws Excep
@DisplayName("findAllByTopicId should return list of all messages by topic id")
public void findAllByTopicId_shouldReturnListOfAllMessagesByTopicId() throws Exception {
// Given
- int topicId = 1;
+ int topicId = 11;
List messages = messageRepository.findAllByTopicId(topicId);
// When
@@ -96,7 +96,7 @@ public void findAllByTopicId_shouldReturnListOfAllMessagesByTopicId() throws Exc
@DisplayName("reportMessage should report a message")
public void reportMessage_shouldReportMessage() throws Exception {
// Given
- int messageId = 3;
+ int messageId = 103;
String email = "vasil@gmail.com";
// When
@@ -126,7 +126,7 @@ public void reportMessage_shouldReturn404ForNonExistingMessage() throws Exceptio
@DisplayName("reportMessage should return 400 Bad Request if message is already reported")
public void reportMessage_shouldReturn400ForAlreadyReportedMessage() throws Exception {
// Given
- int messageId = 1;
+ int messageId = 100;
String email = "vasil@gmail.com";
// When
diff --git a/src/test/java/com/chat/yourway/integration/controller/websocketclient/TestStompFrameHandler.java b/src/test/java/com/chat/yourway/integration/controller/websocketclient/TestStompFrameHandler.java
new file mode 100644
index 00000000..45e88f2f
--- /dev/null
+++ b/src/test/java/com/chat/yourway/integration/controller/websocketclient/TestStompFrameHandler.java
@@ -0,0 +1,33 @@
+package com.chat.yourway.integration.controller.websocketclient;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.lang.reflect.Type;
+import java.util.concurrent.CompletableFuture;
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.jetbrains.annotations.NotNull;
+import org.springframework.messaging.simp.stomp.StompFrameHandler;
+import org.springframework.messaging.simp.stomp.StompHeaders;
+
+@Slf4j
+@AllArgsConstructor
+public class TestStompFrameHandler implements StompFrameHandler {
+
+ private final CompletableFuture resultKeeper;
+ private final ObjectMapper objectMapper;
+ private final Class returnClass;
+
+ @Override
+ public @NotNull Type getPayloadType(@NotNull StompHeaders headers) {
+ return byte[].class;
+ }
+
+ @SneakyThrows
+ @Override
+ public void handleFrame(@NotNull StompHeaders headers, Object payload) {
+ T message = objectMapper.readValue((byte[]) payload, returnClass);
+ log.info("received message: {} with headers: {}", message, headers);
+ resultKeeper.complete(message);
+ }
+}
diff --git a/src/test/java/com/chat/yourway/integration/extension/RedisExtension.java b/src/test/java/com/chat/yourway/integration/extension/RedisExtension.java
index feca86a3..b33b641d 100644
--- a/src/test/java/com/chat/yourway/integration/extension/RedisExtension.java
+++ b/src/test/java/com/chat/yourway/integration/extension/RedisExtension.java
@@ -9,17 +9,14 @@
public class RedisExtension implements Extension, BeforeAllCallback, AfterAllCallback {
- private static final String REDIS_PASSWORD = "12345676890";
private static final RedisContainer container = new RedisContainer(
DockerImageName.parse("redis:7.0.12"));
-
@Override
public void beforeAll(ExtensionContext extensionContext) {
container.start();
System.setProperty("REDIS_HOST", container.getHost());
System.setProperty("REDIS_PORT", String.valueOf(container.getFirstMappedPort()));
- System.setProperty("REDIS_PASSWORD", REDIS_PASSWORD);
}
@Override
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
index 9fe6338e..6c64efe3 100644
--- a/src/test/resources/application.properties
+++ b/src/test/resources/application.properties
@@ -1,3 +1,6 @@
+# Spring Profile:
+spring.profiles.active=test
+
# Database Configuration
spring.datasource.url=${JDBC_URL}
spring.datasource.driver-class-name=org.postgresql.Driver
@@ -7,16 +10,13 @@ spring.datasource.hikari.schema=chat
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
-# Spring Profiles
-spring.profiles.active=test
-
# Flyway
spring.flyway.enabled=true
-# Redis
+#Redis:
spring.data.redis.host=${REDIS_HOST}
spring.data.redis.port=${REDIS_PORT}
-spring.data.redis.password=${REDIS_PASSWORD}
+spring.data.redis.password=${REDIS_PASSWORD:1234}
# Logging Configuration
logging.level.org.springframework=ERROR
@@ -30,7 +30,7 @@ socket.endpoint=/chat
# Security:
security.jwt.token-type=Bearer
-security.jwt.secret-key=jwt-secret-key
+security.jwt.secret-key=D9D323C5E55F45C206D7880329B1721A4334C00F336E5F2F1E9DAB745FF44837
# 1 h. * 60 min. * 60 sec. * 1000 millis.
security.jwt.expiration=3600000
# 7 days
@@ -52,4 +52,7 @@ spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
# Message values
-message.max.amount.reports=2
\ No newline at end of file
+message.max.amount.reports=2
+
+#Logging:
+logging.level.com.chat.yourway=trace
\ No newline at end of file
diff --git a/src/test/resources/dataset/mockdb/contact.xml b/src/test/resources/dataset/mockdb/contact.xml
index ac45a538..0aceb462 100644
--- a/src/test/resources/dataset/mockdb/contact.xml
+++ b/src/test/resources/dataset/mockdb/contact.xml
@@ -1,7 +1,7 @@
diff --git a/src/test/resources/dataset/mockdb/email_token.xml b/src/test/resources/dataset/mockdb/email_token.xml
index 904f3c28..b3132df2 100644
--- a/src/test/resources/dataset/mockdb/email_token.xml
+++ b/src/test/resources/dataset/mockdb/email_token.xml
@@ -3,11 +3,11 @@
diff --git a/src/test/resources/dataset/mockdb/message.xml b/src/test/resources/dataset/mockdb/message.xml
index 2e41fa35..83b84196 100644
--- a/src/test/resources/dataset/mockdb/message.xml
+++ b/src/test/resources/dataset/mockdb/message.xml
@@ -1,35 +1,35 @@
\ No newline at end of file
diff --git a/src/test/resources/dataset/mockdb/tag.xml b/src/test/resources/dataset/mockdb/tag.xml
index e22b49c7..2b85cc13 100644
--- a/src/test/resources/dataset/mockdb/tag.xml
+++ b/src/test/resources/dataset/mockdb/tag.xml
@@ -1,11 +1,11 @@
diff --git a/src/test/resources/dataset/mockdb/topic.xml b/src/test/resources/dataset/mockdb/topic.xml
index 29f1f1a6..6f132406 100644
--- a/src/test/resources/dataset/mockdb/topic.xml
+++ b/src/test/resources/dataset/mockdb/topic.xml
@@ -1,17 +1,31 @@
+
+
\ No newline at end of file
diff --git a/src/test/resources/dataset/mockdb/topic_subscriber.xml b/src/test/resources/dataset/mockdb/topic_subscriber.xml
index 155ff690..55bac039 100644
--- a/src/test/resources/dataset/mockdb/topic_subscriber.xml
+++ b/src/test/resources/dataset/mockdb/topic_subscriber.xml
@@ -1,31 +1,33 @@
+
diff --git a/src/test/resources/dataset/mockdb/topic_tag.xml b/src/test/resources/dataset/mockdb/topic_tag.xml
index 53c2bbbc..bd4b0366 100644
--- a/src/test/resources/dataset/mockdb/topic_tag.xml
+++ b/src/test/resources/dataset/mockdb/topic_tag.xml
@@ -1,11 +1,11 @@