X-Git-Url: http://juplo.de/gitweb/?a=blobdiff_plain;f=src%2Ftest%2Fjava%2Fde%2Fjuplo%2Fkafka%2Fchat%2Fbackend%2Fdomain%2FChatRoomDataTest.java;h=a5c8918f4f0a7e9f819ec671f0ad7c972fc8c793;hb=9bde1226e097e04c09f687a94148c0138a38f5b2;hp=3c7d1dd20eed6e7dc83da7514d0c7b1ba90d4a83;hpb=33143e121d6bc74b1a1e5c19b443049bf027ade4;p=demos%2Fkafka%2Fchat 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 3c7d1dd2..a5c8918f 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,13 +1,15 @@ package de.juplo.kafka.chat.backend.domain; +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.Clock; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; +import java.time.*; +import java.util.LinkedList; +import java.util.List; import static org.mockito.Mockito.*; import static pl.rzrz.assertj.reactor.Assertions.assertThat; @@ -15,43 +17,56 @@ import static pl.rzrz.assertj.reactor.Assertions.assertThat; public class ChatRoomDataTest { + Clock now; + ChatMessageService chatMessageService; + ChatRoomData chatRoomData; + + String user; + Long messageId; + Message.MessageKey key; + LocalDateTime timestamp; + + + @BeforeEach + public void setUp() + { + now = Clock.fixed(Instant.now(), ZoneId.systemDefault()); + chatMessageService = mock(ChatMessageService.class); + chatRoomData = new ChatRoomData( + now, + chatMessageService, + 8); + chatRoomData.activate(); + + user = "foo"; + messageId = 1l; + key = Message.MessageKey.of(user, messageId); + timestamp = LocalDateTime.now(now); + } + + @Test @DisplayName("Assert, that Mono emits expected message, if it exists") void testGetExistingMessage() { // Given - String user = "foo"; - Long messageId = 1l; - ChatMessageService chatMessageService = mock(ChatMessageService.class); - ChatRoomData chatRoomData = new ChatRoomData( - Clock.systemDefaultZone(), - chatMessageService, - 8); - Message.MessageKey key = Message.MessageKey.of(user, messageId); - LocalDateTime timestamp = LocalDateTime.now(); - Message message = new Message(key, 0l, timestamp, "Bar"); - when(chatMessageService.getMessage(any(Message.MessageKey.class))).thenReturn(Mono.just(message)); + when(chatMessageService.getMessage(any(Message.MessageKey.class))) + .thenReturn(Mono.just(someMessage())); // When Mono mono = chatRoomData.getMessage(user, messageId); // Then - assertThat(mono).emitsExactly(message); + assertThat(mono).emitsExactly(someMessage()); } @Test - @DisplayName("Assert, that Mono if empty, if message does not exists") + @DisplayName("Assert, that Mono is empty, if message does not exists") void testGetNonExistentMessage() { // Given - String user = "foo"; - Long messageId = 1l; - ChatMessageService chatMessageService = mock(ChatMessageService.class); - ChatRoomData chatRoomData = new ChatRoomData( - Clock.systemDefaultZone(), - chatMessageService, - 8); - when(chatMessageService.getMessage(any(Message.MessageKey.class))).thenReturn(Mono.empty()); + when(chatMessageService.getMessage(any(Message.MessageKey.class))) + .thenReturn(Mono.empty()); // When Mono mono = chatRoomData.getMessage(user, messageId); @@ -61,79 +76,88 @@ public class ChatRoomDataTest } @Test - @DisplayName("Assert, that Mono emits expected message, if a new message is added") - void testAddNewMessage() + @DisplayName("Assert, that Mono emits the persisted message, if a new message is added") + void testAddNewMessageEmitsPersistedMessage() + { + // 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())); + + // When + Mono mono = chatRoomData.addMessage(messageId, user, "Some Text"); + + // Then + assertThat(mono).emitsExactly(someMessage()); + } + + @Test + @DisplayName("Assert, that ChatMessageService.persistMessage() is called correctly, if a new message is added") + void testAddNewMessageTriggersPersistence() { // Given - String user = "foo"; - Long messageId = 1l; - ChatMessageService chatMessageService = mock(ChatMessageService.class); - ChatRoomData chatRoomData = new ChatRoomData( - Clock.systemDefaultZone(), - chatMessageService, - 8); - Message.MessageKey key = Message.MessageKey.of(user, messageId); - Clock now = Clock.fixed(Instant.now(), ZoneId.systemDefault()); - LocalDateTime timestamp = LocalDateTime.now(now); String messageText = "Bar"; - Message message = new Message(key, 0l, timestamp, messageText); - 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(message)); + 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())); // When - Mono mono = chatRoomData.addMessage(messageId, user, messageText); + chatRoomData + .addMessage(messageId, user, messageText) + .block(); // Then - assertThat(mono).emitsExactly(message); + verify(chatMessageService, times(1)).persistMessage(eq(key), eq(timestamp), eq(messageText)); } @Test - @DisplayName("Assert, that Mono emits expected message, if an unchanged message is added") - void testAddUnchangedMessage() + @DisplayName("Assert, that Mono emits the already persisted message, if an unchanged message is added") + void testAddUnchangedMessageEmitsAlreadyPersistedMessage() { // Given - String user = "foo"; - Long messageId = 1l; - ChatMessageService chatMessageService = mock(ChatMessageService.class); - ChatRoomData chatRoomData = new ChatRoomData( - Clock.systemDefaultZone(), - chatMessageService, - 8); - Message.MessageKey key = Message.MessageKey.of(user, messageId); - Clock now = Clock.fixed(Instant.now(), ZoneId.systemDefault()); - LocalDateTime timestamp = LocalDateTime.now(now); String messageText = "Bar"; - Message message = new Message(key, 0l, timestamp, messageText); - when(chatMessageService.getMessage(any(Message.MessageKey.class))).thenReturn(Mono.just(message)); - when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class))).thenReturn(Mono.just(message)); + Message existingMessage = new Message(key, 0l, timestamp, messageText); + when(chatMessageService.getMessage(any(Message.MessageKey.class))) + .thenReturn(Mono.just(existingMessage)); // When Mono mono = chatRoomData.addMessage(messageId, user, messageText); // Then - assertThat(mono).emitsExactly(message); + assertThat(mono).emitsExactly(existingMessage); + } + + @Test + @DisplayName("Assert, that ChatMessageService.persistMessage() is not called, if an unchanged message is added") + void testAddUnchangedMessageDoesNotTriggerPersistence() + { + // Given + String messageText = "Bar"; + Message existingMessage = new Message(key, 0l, timestamp, messageText); + when(chatMessageService.getMessage(any(Message.MessageKey.class))) + .thenReturn(Mono.just(existingMessage)); + + // When + chatRoomData + .addMessage(messageId, user, messageText) + .block(); + + // Then + verify(chatMessageService, never()).persistMessage(any(), any(), any()); } @Test @DisplayName("Assert, that Mono sends an error, if a message is added again with mutated text") - void testAddMutatedMessage() + void testAddMutatedMessageSendsError() { // Given - String user = "foo"; - Long messageId = 1l; - ChatMessageService chatMessageService = mock(ChatMessageService.class); - ChatRoomData chatRoomData = new ChatRoomData( - Clock.systemDefaultZone(), - chatMessageService, - 8); - Message.MessageKey key = Message.MessageKey.of(user, messageId); - Clock now = Clock.fixed(Instant.now(), ZoneId.systemDefault()); - LocalDateTime timestamp = LocalDateTime.now(now); String messageText = "Bar"; String mutatedText = "Boom!"; - Message message = new Message(key, 0l, timestamp, messageText); - when(chatMessageService.getMessage(any(Message.MessageKey.class))).thenReturn(Mono.just(message)); - when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class))).thenReturn(Mono.just(message)); + Message existingMessage = new Message(key, 0l, timestamp, messageText); + when(chatMessageService.getMessage(any(Message.MessageKey.class))) + .thenReturn(Mono.just(existingMessage)); // When Mono mono = chatRoomData.addMessage(messageId, user, mutatedText); @@ -141,4 +165,272 @@ public class ChatRoomDataTest // Then assertThat(mono).sendsError(); } + + @Test + @DisplayName("Assert, that ChatMessageService.persistMessage() is not called, if a message is added again with mutated text") + void testAddMutatedDoesNotTriggerPersistence() + { + // Given + String messageText = "Bar"; + String mutatedText = "Boom!"; + Message existingMessage = new Message(key, 0l, timestamp, messageText); + when(chatMessageService.getMessage(any(Message.MessageKey.class))) + .thenReturn(Mono.just(existingMessage)); + + // When + chatRoomData + .addMessage(messageId, user, mutatedText) + .onErrorResume((throwable) -> Mono.empty()) + .block(); + + // Then + verify(chatMessageService, never()).persistMessage(any(), any(), any()); + } + + @Test + @DisplayName("Assert, that Mono sends an error, if a message is sent to a closed chat-room") + void testAddMessageToClosedChatRoomSendsError() + { + // 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 a closed chat-room") + void testAddMessageToClosedChatRoomDoesNotTriggerPersistence() + { + // 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(); + + // 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() + { + // 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())); + + // When + List receivedMessages = new LinkedList<>(); + chatRoomData + .listen() + .subscribe(receivedMessage -> receivedMessages.add(receivedMessage)); + Message sentMessage = chatRoomData + .addMessage(messageId, user, "Some Text") + .block(); + + // Then + Awaitility + .await() + .atMost(Duration.ofSeconds(1)) + .untilAsserted(() -> assertThat(receivedMessages).contains(sentMessage)); + } + + @Test + @DisplayName("Assert, that a listener receives a message, that was added before the listening had started") + void testListenerReceivesMessageAddedBeforeListeningStarts() + { + // 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())); + + // When + Message sentMessage = chatRoomData + .addMessage(messageId, user, "Some Text") + .block(); + List receivedMessages = new LinkedList<>(); + chatRoomData + .listen() + .subscribe(receivedMessage -> receivedMessages.add(receivedMessage)); + + // Then + Awaitility + .await() + .atMost(Duration.ofSeconds(1)) + .untilAsserted(() -> assertThat(receivedMessages).contains(sentMessage)); + } + + @Test + @DisplayName("Assert, that a listener receives several messages, that were added before and after the listening had started, in correct order") + void testListenerReceivesMessagesFromBeforeAndAfterListeningHadStartedInCorrectOrder() + { + // 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)); + + // When + Message[] sentMessages = new Message[4]; + sentMessages[0] = chatRoomData.addMessage(messageId, user, "Some Text").block(); + sentMessages[1] = chatRoomData.addMessage(messageId, user, "Some Text").block(); + List receivedMessages = new LinkedList<>(); + chatRoomData + .listen() + .subscribe(receivedMessage -> receivedMessages.add(receivedMessage)); + sentMessages[2] = chatRoomData.addMessage(messageId, user, "Some Text").block(); + sentMessages[3] = chatRoomData.addMessage(messageId, user, "Some Text").block(); + + // Then + Awaitility + .await() + .atMost(Duration.ofSeconds(1)) + .untilAsserted(() -> assertThat(receivedMessages).contains(sentMessages)); + } + + @Test + @DisplayName("Assert, that multiple listeners can receive an added message") + void testMultipleListeners() + { + // 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)); + + // When + Message[] sentMessages = new Message[4]; + List messagesReceivedByListener1 = new LinkedList<>(); + chatRoomData + .listen() + .subscribe(receivedMessage -> messagesReceivedByListener1.add(receivedMessage)); + sentMessages[0] = chatRoomData.addMessage(messageId, user, "Some Text").block(); + sentMessages[1] = chatRoomData.addMessage(messageId, user, "Some Text").block(); + List messagesReceivedByListener2 = new LinkedList<>(); + chatRoomData + .listen() + .subscribe(receivedMessage -> messagesReceivedByListener2.add(receivedMessage)); + sentMessages[2] = chatRoomData.addMessage(messageId, user, "Some Text").block(); + List messagesReceivedByListener3 = new LinkedList<>(); + chatRoomData + .listen() + .subscribe(receivedMessage -> messagesReceivedByListener3.add(receivedMessage)); + sentMessages[3] = chatRoomData.addMessage(messageId, user, "Some Text").block(); + List messagesReceivedByListener4 = new LinkedList<>(); + chatRoomData + .listen() + .subscribe(receivedMessage -> messagesReceivedByListener4.add(receivedMessage)); + + // Then + Awaitility + .await() + .atMost(Duration.ofSeconds(1)) + .untilAsserted(() -> + { + assertThat(messagesReceivedByListener1).contains(sentMessages); + assertThat(messagesReceivedByListener2).contains(sentMessages); + assertThat(messagesReceivedByListener3).contains(sentMessages); + assertThat(messagesReceivedByListener4).contains(sentMessages); + }); + } + + @Test + @DisplayName("Assert, that a listended to chat-room emits completed, if it is closed") + void testListenedToChatRoomEmitsCompletedIfItIsClosed() + { + // 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 + assertThat(listenFlux).emitsExactly( + message1, + message2, + message3, + message4); + } + + @Test + @DisplayName("Assert, that a listended to chat-room emits completed, if it is closed") + void testListeningToClosedChatRoomSendsError() + { + // Given + chatRoomData.deactivate(); + + // When + Flux listenFlux = chatRoomData.listen(); + + // Then + assertThat(listenFlux).sendsError(); + } + + + /** + * This message is used, when methods of {@link ChatMessageService} are mocked, + * that return a {@link Message}. + * The contents of the message are set to arbitrary values, in order to underline + * the fact, that the test can only assert, that the message that was returned + * by {@link ChatMessageService} is handed on by {@link ChatRoomData} correctly. + * @return a message. + */ + private Message someMessage() + { + return new Message( + Message.MessageKey.of("FOO", 666l), + 666l, + LocalDateTime.of(2024, 3, 8, 12, 13, 00), + "Just some message..."); + } }