From d529ca4347dea9bdd6c7f5efbcc3547f547ddb92 Mon Sep 17 00:00:00 2001 From: Kai Moritz Date: Mon, 11 Mar 2024 11:59:50 +0100 Subject: [PATCH] test: RED - Formulated expectations for active vs. inactive `ChatRoomData` * Introduced the methods `ChatRoomData+activate()` and `ChatRoomData#deactivate()`. * Added tests to `ChatRoomDataTest`, that assert the expectations. * Added tests to `AbstractConfigurationIT`, that assert the expectations. --- .../chat/backend/domain/ChatRoomData.java | 8 ++ .../chat/backend/AbstractConfigurationIT.java | 56 ++++++++++- ...thNoStorageAndShardingConfigurationIT.java | 4 + .../InMemoryWithNoStorageConfigurationIT.java | 4 + .../chat/backend/domain/ChatRoomDataTest.java | 95 ++++++++++++++++++- 5 files changed, 165 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/juplo/kafka/chat/backend/domain/ChatRoomData.java b/src/main/java/de/juplo/kafka/chat/backend/domain/ChatRoomData.java index 9dbeda9e..1edae4d7 100644 --- a/src/main/java/de/juplo/kafka/chat/backend/domain/ChatRoomData.java +++ b/src/main/java/de/juplo/kafka/chat/backend/domain/ChatRoomData.java @@ -106,6 +106,14 @@ public class ChatRoomData return service.getMessages(first, last); } + public void activate() + { + } + + public void deactivate() + { + } + private Sinks.Many createSink() { return Sinks diff --git a/src/test/java/de/juplo/kafka/chat/backend/AbstractConfigurationIT.java b/src/test/java/de/juplo/kafka/chat/backend/AbstractConfigurationIT.java index 452e4244..451690b2 100644 --- a/src/test/java/de/juplo/kafka/chat/backend/AbstractConfigurationIT.java +++ b/src/test/java/de/juplo/kafka/chat/backend/AbstractConfigurationIT.java @@ -1,22 +1,29 @@ package de.juplo.kafka.chat.backend; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import de.juplo.kafka.chat.backend.api.ChatRoomInfoTo; +import de.juplo.kafka.chat.backend.api.MessageTo; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.core.io.Resource; import org.springframework.http.MediaType; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.web.reactive.server.WebTestClient; import org.testcontainers.shaded.org.awaitility.Awaitility; +import reactor.core.publisher.Flux; import java.io.IOException; import java.time.Duration; +import java.util.List; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.random.RandomGenerator; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.endsWith; @@ -37,10 +44,20 @@ public abstract class AbstractConfigurationIT @Autowired ObjectMapper objectMapper; + @Value("classpath:data/files/5c73531c-6fc4-426c-adcb-afc5c140a0f7.json") + Resource existingChatRoomRessource; + MessageTo[] expectedExistingMessages; + @BeforeEach - void waitForApp() + void waitForApp() throws IOException { + expectedExistingMessages = objectMapper + .readValue( + existingChatRoomRessource.getInputStream(), + new TypeReference>() {}) + .toArray(size -> new MessageTo[size]); + Awaitility .await() .atMost(Duration.ofSeconds(15)) @@ -232,4 +249,41 @@ public abstract class AbstractConfigurationIT .jsonPath("$.text").isEqualTo("Hello world!"); }); } + + @Test + @DisplayName("Only newly send messages can be seen, when listening to restored chat-room") + void testListenToRestoredChatRoomYieldsOnlyNewlyAddedMessages() + { + MessageTo sentMessage = webTestClient + .put() + .uri( + "http://localhost:{port}/{chatRoomId}/nerd/{messageId}", + port, + EXISTING_CHATROOM, + RandomGenerator.getDefault().nextInt()) + .contentType(MediaType.TEXT_PLAIN) + .accept(MediaType.APPLICATION_JSON) + .bodyValue("Hello world!") + .exchange() + .expectStatus() + .isOk() + .returnResult(MessageTo.class) + .getResponseBody() + .next() + .block(); + + Flux result = webTestClient + .get() + .uri( + "http://localhost:{port}/{chatRoomId}/listen", + port, + EXISTING_CHATROOM) + .accept(MediaType.TEXT_EVENT_STREAM) + .exchange() + .expectStatus().isOk() + .returnResult(MessageTo.class) + .getResponseBody(); + + assertThat(result.next().block()).isEqualTo(sentMessage); + } } diff --git a/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageAndShardingConfigurationIT.java b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageAndShardingConfigurationIT.java index fb364182..5696d7ef 100644 --- a/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageAndShardingConfigurationIT.java +++ b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageAndShardingConfigurationIT.java @@ -28,4 +28,8 @@ class InMemoryWithNoStorageAndShardingConfigurationIT extends AbstractConfigurat @Override @Disabled("Chat-Rooms cannot be restored, if storage is disabled") void testRestoredMessageForPeterHasExpectedText() {} + + @Override + @Disabled("Chat-Rooms cannot be restored, if storage is disabled") + void testListenToRestoredChatRoomYieldsOnlyNewlyAddedMessages() {} } diff --git a/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageConfigurationIT.java b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageConfigurationIT.java index 89748a18..90c0b8d6 100644 --- a/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageConfigurationIT.java +++ b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageConfigurationIT.java @@ -26,4 +26,8 @@ class InMemoryWithNoStorageConfigurationIT extends AbstractConfigurationIT @Override @Disabled("Chat-Rooms cannot be restored, if storage is disabled") void testRestoredMessageForPeterHasExpectedText() {} + + @Override + @Disabled("Chat-Rooms cannot be restored, if storage is disabled") + void testListenToRestoredChatRoomYieldsOnlyNewlyAddedMessages() {} } diff --git a/src/test/java/de/juplo/kafka/chat/backend/domain/ChatRoomDataTest.java b/src/test/java/de/juplo/kafka/chat/backend/domain/ChatRoomDataTest.java index 4a7f2d64..57b8e59c 100644 --- a/src/test/java/de/juplo/kafka/chat/backend/domain/ChatRoomDataTest.java +++ b/src/test/java/de/juplo/kafka/chat/backend/domain/ChatRoomDataTest.java @@ -1,9 +1,10 @@ package de.juplo.kafka.chat.backend.domain; -import org.junit.jupiter.api.BeforeEach; import org.awaitility.Awaitility; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.time.*; @@ -35,6 +36,7 @@ public class ChatRoomDataTest now, chatMessageService, 8); + chatRoomData.activate(); user = "foo"; messageId = 1l; @@ -185,6 +187,47 @@ public class ChatRoomDataTest verify(chatMessageService, never()).persistMessage(any(), any(), any()); } + @Test + @DisplayName("Assert, that Mono sends an error, if a message is sent to an inactive chat-room") + void testAddMessageToInactiveChatRoomSendsError() + { + // Given + when(chatMessageService.getMessage(any(Message.MessageKey.class))) + .thenReturn(Mono.empty()); + when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class))) + .thenReturn(Mono.just(someMessage())); + + chatRoomData.deactivate(); + + // When + Mono mono = chatRoomData.addMessage(messageId, user, "Some text"); + + // Then + assertThat(mono).sendsError(); + } + + @Test + @DisplayName("Assert, that ChatMessageService.persistMessage() is not called if a message is sent to an inactive chat-room") + void testAddMessageToInactiveChatRoomDoesNotTriggerPersistence() + { + // Given + when(chatMessageService.getMessage(any(Message.MessageKey.class))) + .thenReturn(Mono.empty()); + when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class))) + .thenReturn(Mono.just(someMessage())); + + chatRoomData.deactivate(); + + // When + chatRoomData + .addMessage(messageId, user, "Some text") + .onErrorResume((throwable) -> Mono.empty()) + .block(Duration.ofSeconds(5)); + + // Then + verify(chatMessageService, never()).persistMessage(any(), any(), any()); + } + @Test @DisplayName("Assert, that a listener receives a message, that was added after the listening had started") void testListenerReceivesMessageAddedAfterListeningStarts() @@ -325,6 +368,56 @@ public class ChatRoomDataTest }); } + @Test + @DisplayName("Assert, that a listended to chat-room emits completed, if it is deactivated") + void testListenedToChatRoomEmitsCompletedIfItIsDeactivated() + { + // Given + Message message1 = new Message(key, 1l, timestamp, "#1"); + Message message2 = new Message(key, 2l, timestamp, "#2"); + Message message3 = new Message(key, 3l, timestamp, "#3"); + Message message4 = new Message(key, 4l, timestamp, "#4"); + when(chatMessageService.getMessage(any(Message.MessageKey.class))) + .thenReturn(Mono.empty()); + when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class))) + .thenReturn(Mono.just(message1)) + .thenReturn(Mono.just(message2)) + .thenReturn(Mono.just(message3)) + .thenReturn(Mono.just(message4)); + + chatRoomData.addMessage(messageId, user, "Some Text").block(); + chatRoomData.addMessage(messageId, user, "Some Text").block(); + chatRoomData.addMessage(messageId, user, "Some Text").block(); + chatRoomData.addMessage(messageId, user, "Some Text").block(); + + // When + Flux listenFlux = chatRoomData.listen(); + chatRoomData.deactivate(); + + // Then + Awaitility + .await() + .atMost(Duration.ofSeconds(1)) + .untilAsserted(() -> assertThat(listenFlux).emitsExactly(message1, message2, message3, message4)); + } + + @Test + @DisplayName("Assert, that a listended to chat-room emits completed, if it is inactiv") + void testListeningToInactiveChatRoomSendsError() + { + // Given + chatRoomData.deactivate(); + + // When + Flux listenFlux = chatRoomData.listen(); + + // Then + Awaitility + .await() + .atMost(Duration.ofSeconds(1)) + .untilAsserted(() -> assertThat(listenFlux).sendsError()); + } + /** * This message is used, when methods of {@link ChatMessageService} are mocked, -- 2.20.1