1c11f936ba14120c3190e574ae79c31c03305a3f
[demos/kafka/chat] / src / test / java / de / juplo / kafka / chat / backend / domain / ChatRoomDataTest.java
1 package de.juplo.kafka.chat.backend.domain;
2
3 import org.junit.jupiter.api.BeforeEach;
4 import org.awaitility.Awaitility;
5 import org.junit.jupiter.api.DisplayName;
6 import org.junit.jupiter.api.Test;
7 import reactor.core.publisher.Mono;
8
9 import java.time.*;
10 import java.util.LinkedList;
11 import java.util.List;
12
13 import static org.mockito.Mockito.*;
14 import static pl.rzrz.assertj.reactor.Assertions.assertThat;
15
16
17 public class ChatRoomDataTest
18 {
19   Clock now;
20   ChatMessageService chatMessageService;
21   ChatRoomData chatRoomData;
22
23   String user;
24   Long messageId;
25   Message.MessageKey key;
26   LocalDateTime timestamp;
27
28
29   @BeforeEach
30   public void setUp()
31   {
32     now = Clock.fixed(Instant.now(), ZoneId.systemDefault());
33     chatMessageService = mock(ChatMessageService.class);
34     chatRoomData = new ChatRoomData(
35         now,
36         chatMessageService,
37         8);
38
39     user = "foo";
40     messageId = 1l;
41     key = Message.MessageKey.of(user, messageId);
42     timestamp = LocalDateTime.now(now);
43   }
44
45
46   @Test
47   @DisplayName("Assert, that Mono emits expected message, if it exists")
48   void testGetExistingMessage()
49   {
50     // Given
51     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
52         .thenReturn(Mono.just(someMessage()));
53
54     // When
55     Mono<Message> mono = chatRoomData.getMessage(user, messageId);
56
57     // Then
58     assertThat(mono).emitsExactly(someMessage());
59   }
60
61   @Test
62   @DisplayName("Assert, that Mono is empty, if message does not exists")
63   void testGetNonExistentMessage()
64   {
65     // Given
66     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
67         .thenReturn(Mono.empty());
68
69     // When
70     Mono<Message> mono = chatRoomData.getMessage(user, messageId);
71
72     // Then
73     assertThat(mono).emitsCount(0);
74   }
75
76   @Test
77   @DisplayName("Assert, that Mono emits the persisted message, if a new message is added")
78   void testAddNewMessageEmitsPersistedMessage()
79   {
80     // Given
81     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
82         .thenReturn(Mono.empty());
83     when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
84         .thenReturn(Mono.just(someMessage()));
85
86     // When
87     Mono<Message> mono = chatRoomData.addMessage(messageId, user, "Some Text");
88
89     // Then
90     assertThat(mono).emitsExactly(someMessage());
91   }
92
93   @Test
94   @DisplayName("Assert, that ChatMessageService.persistMessage() is called correctly, if a new message is added")
95   void testAddNewMessageTriggersPersistence()
96   {
97     // Given
98     String messageText = "Bar";
99     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
100         .thenReturn(Mono.empty());
101     when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
102         .thenReturn(Mono.just(someMessage()));
103
104     // When
105     chatRoomData
106         .addMessage(messageId, user, messageText)
107         .block();
108
109     // Then
110     verify(chatMessageService, times(1)).persistMessage(eq(key), eq(timestamp), eq(messageText));
111   }
112
113   @Test
114   @DisplayName("Assert, that Mono emits the already persisted message, if an unchanged message is added")
115   void testAddUnchangedMessageEmitsAlreadyPersistedMessage()
116   {
117     // Given
118     String messageText = "Bar";
119     Message existingMessage = new Message(key, 0l, timestamp, messageText);
120     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
121         .thenReturn(Mono.just(existingMessage));
122
123     // When
124     Mono<Message> mono = chatRoomData.addMessage(messageId, user, messageText);
125
126     // Then
127     assertThat(mono).emitsExactly(existingMessage);
128   }
129
130   @Test
131   @DisplayName("Assert, that ChatMessageService.persistMessage() is not called, if an unchanged message is added")
132   void testAddUnchangedMessageDoesNotTriggerPersistence()
133   {
134     // Given
135     String messageText = "Bar";
136     Message existingMessage = new Message(key, 0l, timestamp, messageText);
137     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
138         .thenReturn(Mono.just(existingMessage));
139
140     // When
141     chatRoomData
142         .addMessage(messageId, user, messageText)
143         .block();
144
145     // Then
146     verify(chatMessageService, never()).persistMessage(any(), any(), any());
147   }
148
149   @Test
150   @DisplayName("Assert, that Mono sends an error, if a message is added again with mutated text")
151   void testAddMutatedMessageSendsError()
152   {
153     // Given
154     String messageText = "Bar";
155     String mutatedText = "Boom!";
156     Message existingMessage = new Message(key, 0l, timestamp, messageText);
157     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
158         .thenReturn(Mono.just(existingMessage));
159
160     // When
161     Mono<Message> mono = chatRoomData.addMessage(messageId, user, mutatedText);
162
163     // Then
164     assertThat(mono).sendsError();
165   }
166
167   @Test
168   @DisplayName("Assert, that ChatMessageService.persistMessage() is not called, if a message is added again with mutated text")
169   void testAddMutatedDoesNotTriggerPersistence()
170   {
171     // Given
172     String messageText = "Bar";
173     String mutatedText = "Boom!";
174     Message existingMessage = new Message(key, 0l, timestamp, messageText);
175     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
176         .thenReturn(Mono.just(existingMessage));
177
178     // When
179     chatRoomData
180         .addMessage(messageId, user, mutatedText)
181         .onErrorResume((throwable) -> Mono.empty())
182         .block();
183
184     // Then
185     verify(chatMessageService, never()).persistMessage(any(), any(), any());
186   }
187
188   @Test
189   @DisplayName("Assert, that a listener receives a message, that was added after the listening had started")
190   void testListenerReceivesMessageAddedAfterListeningStarts()
191   {
192     // Given
193     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
194         .thenReturn(Mono.empty());
195     when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
196         .thenReturn(Mono.just(someMessage()));
197
198     // When
199     List<Message> receivedMessages = new LinkedList<>();
200     chatRoomData
201         .listen()
202         .subscribe(receivedMessage -> receivedMessages.add(receivedMessage));
203     Message sentMessage = chatRoomData
204         .addMessage(messageId, user, "Some Text")
205         .block();
206
207     // Then
208     Awaitility
209         .await()
210         .atMost(Duration.ofSeconds(1))
211         .untilAsserted(() -> assertThat(receivedMessages).contains(sentMessage));
212   }
213
214   @Test
215   @DisplayName("Assert, that a listener receives a message, that was added before the listening had started")
216   void testListenerReceivesMessageAddedBeforeListeningStarts()
217   {
218     // Given
219     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
220         .thenReturn(Mono.empty());
221     when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
222         .thenReturn(Mono.just(someMessage()));
223
224     // When
225     Message sentMessage = chatRoomData
226         .addMessage(messageId, user, "Some Text")
227         .block();
228     List<Message> receivedMessages = new LinkedList<>();
229     chatRoomData
230         .listen()
231         .subscribe(receivedMessage -> receivedMessages.add(receivedMessage));
232
233     // Then
234     Awaitility
235         .await()
236         .atMost(Duration.ofSeconds(1))
237         .untilAsserted(() -> assertThat(receivedMessages).contains(sentMessage));
238   }
239
240   @Test
241   @DisplayName("Assert, that a listener receives several messages, that were added before and after the listening had started, in correct order")
242   void testListenerReceivesMessagesFromBeforeAndAfterListeningHadStartedInCorrectOrder()
243   {
244     // Given
245     Message message1 = new Message(key, 1l, timestamp, "#1");
246     Message message2 = new Message(key, 2l, timestamp, "#2");
247     Message message3 = new Message(key, 3l, timestamp, "#3");
248     Message message4 = new Message(key, 4l, timestamp, "#4");
249     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
250         .thenReturn(Mono.empty());
251     when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
252         .thenReturn(Mono.just(message1))
253         .thenReturn(Mono.just(message2))
254         .thenReturn(Mono.just(message3))
255         .thenReturn(Mono.just(message4));
256
257     // When
258     Message[] sentMessages = new Message[4];
259     sentMessages[0] = chatRoomData.addMessage(messageId, user, "Some Text").block();
260     sentMessages[1] = chatRoomData.addMessage(messageId, user, "Some Text").block();
261     List<Message> receivedMessages = new LinkedList<>();
262     chatRoomData
263         .listen()
264         .subscribe(receivedMessage -> receivedMessages.add(receivedMessage));
265     sentMessages[2] = chatRoomData.addMessage(messageId, user, "Some Text").block();
266     sentMessages[3] = chatRoomData.addMessage(messageId, user, "Some Text").block();
267
268     // Then
269     Awaitility
270         .await()
271         .atMost(Duration.ofSeconds(1))
272         .untilAsserted(() -> assertThat(receivedMessages).contains(sentMessages));
273   }
274
275   @Test
276   @DisplayName("Assert, that multiple listeners can receive an added message")
277   void testMultipleListeners()
278   {
279     // Given
280     Message message1 = new Message(key, 1l, timestamp, "#1");
281     Message message2 = new Message(key, 2l, timestamp, "#2");
282     Message message3 = new Message(key, 3l, timestamp, "#3");
283     Message message4 = new Message(key, 4l, timestamp, "#4");
284     when(chatMessageService.getMessage(any(Message.MessageKey.class)))
285         .thenReturn(Mono.empty());
286     when(chatMessageService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
287         .thenReturn(Mono.just(message1))
288         .thenReturn(Mono.just(message2))
289         .thenReturn(Mono.just(message3))
290         .thenReturn(Mono.just(message4));
291
292     // When
293     Message[] sentMessages = new Message[4];
294     List<Message> messagesReceivedByListener1 = new LinkedList<>();
295     chatRoomData
296         .listen()
297         .subscribe(receivedMessage -> messagesReceivedByListener1.add(receivedMessage));
298     sentMessages[0] = chatRoomData.addMessage(messageId, user, "Some Text").block();
299     sentMessages[1] = chatRoomData.addMessage(messageId, user, "Some Text").block();
300     List<Message> messagesReceivedByListener2 = new LinkedList<>();
301     chatRoomData
302         .listen()
303         .subscribe(receivedMessage -> messagesReceivedByListener2.add(receivedMessage));
304     sentMessages[2] = chatRoomData.addMessage(messageId, user, "Some Text").block();
305     List<Message> messagesReceivedByListener3 = new LinkedList<>();
306     chatRoomData
307         .listen()
308         .subscribe(receivedMessage -> messagesReceivedByListener3.add(receivedMessage));
309     sentMessages[3] = chatRoomData.addMessage(messageId, user, "Some Text").block();
310     List<Message> messagesReceivedByListener4 = new LinkedList<>();
311     chatRoomData
312         .listen()
313         .subscribe(receivedMessage -> messagesReceivedByListener4.add(receivedMessage));
314
315     // Then
316     Awaitility
317         .await()
318         .atMost(Duration.ofSeconds(1))
319         .untilAsserted(() ->
320         {
321           assertThat(messagesReceivedByListener1).contains(sentMessages);
322           assertThat(messagesReceivedByListener2).contains(sentMessages);
323           assertThat(messagesReceivedByListener3).contains(sentMessages);
324           assertThat(messagesReceivedByListener4).contains(sentMessages);
325         });
326   }
327
328
329   /**
330    * This message is used, when methods of {@link ChatMessageService} are mocked,
331    * that return a {@link Message}.
332    * The contents of the message are set to arbitrary values, in order to underline
333    * the fact, that the test can only assert, that the message that was returned
334    * by {@link ChatMessageService} is handed on by {@link ChatRoomData} correctly.
335    * @return a message.
336    */
337   private Message someMessage()
338   {
339     return new Message(
340         Message.MessageKey.of("FOO", 666l),
341         666l,
342         LocalDateTime.of(2024, 3, 8, 12, 13, 00),
343         "Just some message...");
344   }
345 }