test: Simplified `ChatBackendControllerTest`
[demos/kafka/chat] / src / test / java / de / juplo / kafka / chat / backend / api / ChatBackendControllerTest.java
1 package de.juplo.kafka.chat.backend.api;
2
3 import de.juplo.kafka.chat.backend.ChatBackendProperties;
4 import de.juplo.kafka.chat.backend.domain.*;
5 import lombok.extern.slf4j.Slf4j;
6 import org.junit.jupiter.api.DisplayName;
7 import org.junit.jupiter.api.Test;
8 import org.springframework.beans.factory.annotation.Autowired;
9 import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
10 import org.springframework.boot.test.context.SpringBootTest;
11 import org.springframework.boot.test.mock.mockito.MockBean;
12 import org.springframework.http.MediaType;
13 import org.springframework.test.web.reactive.server.WebTestClient;
14 import reactor.core.publisher.Mono;
15
16 import java.time.Clock;
17 import java.time.LocalDateTime;
18 import java.util.UUID;
19
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.*;
22
23
24 @SpringBootTest(properties = {
25     "spring.main.allow-bean-definition-overriding=true",
26     })
27 @AutoConfigureWebTestClient
28 @Slf4j
29 public class ChatBackendControllerTest
30 {
31   @Autowired
32   ChatBackendProperties properties;
33
34   @MockBean
35   ChatHome chatHome;
36   @MockBean
37   ChatRoomService chatRoomService;
38
39   @Test
40   @DisplayName("Assert expected problem-details for unknown chatroom on GET /list/{chatroomId}")
41   void testUnknownChatroomExceptionForListChatroom(@Autowired WebTestClient client)
42   {
43     // Given
44     UUID chatroomId = UUID.randomUUID();
45     when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new UnknownChatroomException(chatroomId));
46
47     // When
48     WebTestClient.ResponseSpec responseSpec = client
49         .get()
50         .uri("/{chatroomId}/list", chatroomId)
51         .accept(MediaType.APPLICATION_JSON)
52         .exchange();
53
54     // Then
55     assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
56   }
57
58
59   @Test
60   @DisplayName("Assert expected problem-details for unknown chatroom on GET /get/{chatroomId}")
61   void testUnknownChatroomExceptionForGetChatroom(@Autowired WebTestClient client)
62   {
63     // Given
64     UUID chatroomId = UUID.randomUUID();
65     when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new UnknownChatroomException(chatroomId));
66
67     // When
68     WebTestClient.ResponseSpec responseSpec = client
69         .get()
70         .uri("/{chatroomId}", chatroomId)
71         .accept(MediaType.APPLICATION_JSON)
72         .exchange();
73
74     // Then
75     assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
76   }
77
78   @Test
79   @DisplayName("Assert expected problem-details for unknown chatroom on PUT /put/{chatroomId}/{username}/{messageId}")
80   void testUnknownChatroomExceptionForPutMessage(@Autowired WebTestClient client)
81   {
82     // Given
83     UUID chatroomId = UUID.randomUUID();
84     String username = "foo";
85     Long messageId = 66l;
86     when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new UnknownChatroomException(chatroomId));
87
88     // When
89     WebTestClient.ResponseSpec responseSpec = client
90         .put()
91         .uri(
92             "/{chatroomId}/{username}/{messageId}",
93             chatroomId,
94             username,
95             messageId)
96         .bodyValue("bar")
97         .accept(MediaType.APPLICATION_JSON)
98         .exchange();
99
100     // Then
101     assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
102   }
103
104   @Test
105   @DisplayName("Assert expected problem-details for unknown chatroom on GET /get/{chatroomId}/{username}/{messageId}")
106   void testUnknownChatroomExceptionForGetMessage(@Autowired WebTestClient client)
107   {
108     // Given
109     UUID chatroomId = UUID.randomUUID();
110     String username = "foo";
111     Long messageId = 66l;
112     when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new UnknownChatroomException(chatroomId));
113
114     // When
115     WebTestClient.ResponseSpec responseSpec = client
116         .get()
117         .uri(
118             "/{chatroomId}/{username}/{messageId}",
119             chatroomId,
120             username,
121             messageId)
122         .accept(MediaType.APPLICATION_JSON)
123         .exchange();
124
125     // Then
126     assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
127   }
128
129   @Test
130   @DisplayName("Assert expected problem-details for unknown chatroom on GET /listen/{chatroomId}")
131   void testUnknownChatroomExceptionForListenChatroom(@Autowired WebTestClient client)
132   {
133     // Given
134     UUID chatroomId = UUID.randomUUID();
135     when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new UnknownChatroomException(chatroomId));
136
137     // When
138     WebTestClient.ResponseSpec responseSpec = client
139         .get()
140         .uri("/{chatroomId}/listen", chatroomId)
141         // .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON) << TODO: Does not work!
142         .exchange();
143
144     // Then
145     assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
146   }
147
148   private void assertProblemDetailsForUnknownChatroomException(
149       WebTestClient.ResponseSpec responseSpec,
150       UUID chatroomId)
151   {
152     responseSpec
153         .expectStatus().isNotFound()
154         .expectBody()
155         .jsonPath("$.type").isEqualTo("/problem/unknown-chatroom")
156         .jsonPath("$.chatroomId").isEqualTo(chatroomId.toString());
157   }
158
159   @Test
160   @DisplayName("Assert expected problem-details for message mutation on PUT /put/{chatroomId}/{username}/{messageId}")
161   void testMessageMutationException(@Autowired WebTestClient client)
162   {
163     // Given
164     UUID chatroomId = UUID.randomUUID();
165     String user = "foo";
166     Long messageId = 66l;
167     Message.MessageKey key = Message.MessageKey.of(user, messageId);
168     Long serialNumberExistingMessage = 0l;
169     String timeExistingMessageAsString = "2023-01-09T20:44:57.389665447";
170     LocalDateTime timeExistingMessage = LocalDateTime.parse(timeExistingMessageAsString);
171     String textExistingMessage = "Existing";
172     String textMutatedMessage = "Mutated!";
173     ChatRoom chatRoom = new ChatRoom(
174         chatroomId,
175         "Test-ChatRoom",
176         0,
177         Clock.systemDefaultZone(),
178         chatRoomService, 8);
179     when(chatHome.getChatRoom(eq(chatroomId))).thenReturn(Mono.just(chatRoom));
180     Message existingMessage = new Message(
181         key,
182         serialNumberExistingMessage,
183         timeExistingMessage,
184         textExistingMessage);
185     when(chatRoomService.getMessage(any(Message.MessageKey.class)))
186         .thenReturn(Mono.just(existingMessage));
187     // Needed for readable error-reports, in case of a bug that leads to according unwanted call
188     when(chatRoomService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
189         .thenReturn(Mono.just(mock(Message.class)));
190
191     // When
192     client
193         .put()
194         .uri(
195             "/{chatroomId}/{username}/{messageId}",
196             chatroomId,
197             user,
198             messageId)
199         .bodyValue(textMutatedMessage)
200         .accept(MediaType.APPLICATION_JSON)
201         .exchange()
202         // Then
203         .expectStatus().is4xxClientError()
204         .expectBody()
205         .jsonPath("$.type").isEqualTo("/problem/message-mutation")
206         .jsonPath("$.existingMessage.id").isEqualTo(messageId)
207         .jsonPath("$.existingMessage.serial").isEqualTo(serialNumberExistingMessage)
208         .jsonPath("$.existingMessage.time").isEqualTo(timeExistingMessageAsString)
209         .jsonPath("$.existingMessage.user").isEqualTo(user)
210         .jsonPath("$.existingMessage.text").isEqualTo(textExistingMessage)
211         .jsonPath("$.mutatedText").isEqualTo(textMutatedMessage);
212     verify(chatRoomService, never()).persistMessage(eq(key), any(LocalDateTime.class), any(String.class));
213   }
214
215   @Test
216   @DisplayName("Assert expected problem-details for invalid username on PUT /put/{chatroomId}/{username}/{messageId}")
217   void testInvalidUsernameException(@Autowired WebTestClient client)
218   {
219     // Given
220     UUID chatroomId = UUID.randomUUID();
221     String user = "Foo";
222     Long messageId = 66l;
223     Message.MessageKey key = Message.MessageKey.of(user, messageId);
224     String textMessage = "Hallo Welt";
225     ChatRoom chatRoom = new ChatRoom(
226         chatroomId,
227         "Test-ChatRoom",
228         0,
229         Clock.systemDefaultZone(),
230         chatRoomService, 8);
231     when(chatHome.getChatRoom(any(UUID.class)))
232         .thenReturn(Mono.just(chatRoom));
233     when(chatRoomService.getMessage(any(Message.MessageKey.class)))
234         .thenReturn(Mono.empty());
235     // Needed for readable error-reports, in case of a bug that leads to according unwanted call
236     when(chatRoomService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
237         .thenReturn(Mono.just(mock(Message.class)));
238
239     // When
240     client
241         .put()
242         .uri(
243             "/{chatroomId}/{username}/{messageId}",
244             chatroomId,
245             user,
246             messageId)
247         .bodyValue(textMessage)
248         .accept(MediaType.APPLICATION_JSON)
249         .exchange()
250         // Then
251         .expectStatus().is4xxClientError()
252         .expectBody()
253         .jsonPath("$.type").isEqualTo("/problem/invalid-username")
254         .jsonPath("$.username").isEqualTo(user);
255     verify(chatRoomService, never()).persistMessage(eq(key), any(LocalDateTime.class), any(String.class));
256   }
257
258   @Test
259   @DisplayName("Assert expected problem-details for not owned shard on GET /{chatroomId}")
260   void testShardNotOwnedExceptionForGetChatroom(@Autowired WebTestClient client)
261   {
262     // Given
263     UUID chatroomId = UUID.randomUUID();
264     int shard = 666;
265     when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
266
267     // When
268     WebTestClient.ResponseSpec responseSpec = client
269         .get()
270         .uri("/{chatroomId}", chatroomId)
271         .accept(MediaType.APPLICATION_JSON)
272         .exchange();
273
274     // Then
275     assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
276   }
277
278   @Test
279   @DisplayName("Assert expected problem-details for not owned shard on GET /list/{chatroomId}")
280   void testShardNotOwnedExceptionForListChatroom(@Autowired WebTestClient client)
281   {
282     // Given
283     UUID chatroomId = UUID.randomUUID();
284     int shard = 666;
285     when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
286
287     // When
288     WebTestClient.ResponseSpec responseSpec = client
289         .get()
290         .uri("/{chatroomId}/list", chatroomId)
291         .accept(MediaType.APPLICATION_JSON)
292         .exchange();
293
294     // Then
295     assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
296   }
297
298   @Test
299   @DisplayName("Assert expected problem-details for not owned shard on PUT /put/{chatroomId}/{username}/{messageId}")
300   void testShardNotOwnedExceptionForPutMessage(@Autowired WebTestClient client)
301   {
302     // Given
303     UUID chatroomId = UUID.randomUUID();
304     String username = "foo";
305     Long messageId = 66l;
306     int shard = 666;
307     when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
308
309     // When
310     WebTestClient.ResponseSpec responseSpec = client
311         .put()
312         .uri(
313             "/{chatroomId}/{username}/{messageId}",
314             chatroomId,
315             username,
316             messageId)
317         .bodyValue("bar")
318         .accept(MediaType.APPLICATION_JSON)
319         .exchange();
320
321     // Then
322     assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
323   }
324
325   @Test
326   @DisplayName("Assert expected problem-details for not owned shard on GET /get/{chatroomId}/{username}/{messageId}")
327   void testShardNotOwnedExceptionForGetMessage(@Autowired WebTestClient client)
328   {
329     // Given
330     UUID chatroomId = UUID.randomUUID();
331     String username = "foo";
332     Long messageId = 66l;
333     int shard = 666;
334     when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
335
336     // When
337     WebTestClient.ResponseSpec responseSpec = client
338         .get()
339         .uri(
340             "/{chatroomId}/{username}/{messageId}",
341             chatroomId,
342             username,
343             messageId)
344         .accept(MediaType.APPLICATION_JSON)
345         .exchange();
346
347     // Then
348     assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
349   }
350
351   @Test
352   @DisplayName("Assert expected problem-details for not owned shard on GET /listen/{chatroomId}")
353   void testShardNotOwnedExceptionForListenChatroom(@Autowired WebTestClient client)
354   {
355     // Given
356     UUID chatroomId = UUID.randomUUID();
357     int shard = 666;
358     when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
359
360     // When
361     WebTestClient.ResponseSpec responseSpec = client
362         .get()
363         .uri("/{chatroomId}/listen", chatroomId)
364         // .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON) << TODO: Does not work!
365         .exchange();
366
367     // Then
368     assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
369   }
370
371   private void assertProblemDetailsForShardNotOwnedException(
372       WebTestClient.ResponseSpec responseSpec,
373       int shard)
374   {
375     responseSpec
376         .expectStatus().isNotFound()
377         .expectBody()
378         .jsonPath("$.type").isEqualTo("/problem/shard-not-owned")
379         .jsonPath("$.shard").isEqualTo(shard);
380   }
381 }