a7e407814c03340eb26906fce9814cb35afedab4
[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.getChatRoomData(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.getChatRoomInfo(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.getChatRoomData(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.getChatRoomData(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.getChatRoomData(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     ChatRoomData chatRoomData = new ChatRoomData(
174         Clock.systemDefaultZone(),
175         chatRoomService,
176         8);
177     when(chatHome.getChatRoomData(eq(chatroomId))).thenReturn(Mono.just(chatRoomData));
178     Message existingMessage = new Message(
179         key,
180         serialNumberExistingMessage,
181         timeExistingMessage,
182         textExistingMessage);
183     when(chatRoomService.getMessage(any(Message.MessageKey.class)))
184         .thenReturn(Mono.just(existingMessage));
185     // Needed for readable error-reports, in case of a bug that leads to according unwanted call
186     when(chatRoomService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
187         .thenReturn(Mono.just(mock(Message.class)));
188
189     // When
190     client
191         .put()
192         .uri(
193             "/{chatroomId}/{username}/{messageId}",
194             chatroomId,
195             user,
196             messageId)
197         .bodyValue(textMutatedMessage)
198         .accept(MediaType.APPLICATION_JSON)
199         .exchange()
200         // Then
201         .expectStatus().is4xxClientError()
202         .expectBody()
203         .jsonPath("$.type").isEqualTo("/problem/message-mutation")
204         .jsonPath("$.existingMessage.id").isEqualTo(messageId)
205         .jsonPath("$.existingMessage.serial").isEqualTo(serialNumberExistingMessage)
206         .jsonPath("$.existingMessage.time").isEqualTo(timeExistingMessageAsString)
207         .jsonPath("$.existingMessage.user").isEqualTo(user)
208         .jsonPath("$.existingMessage.text").isEqualTo(textExistingMessage)
209         .jsonPath("$.mutatedText").isEqualTo(textMutatedMessage);
210     verify(chatRoomService, never()).persistMessage(eq(key), any(LocalDateTime.class), any(String.class));
211   }
212
213   @Test
214   @DisplayName("Assert expected problem-details for invalid username on PUT /put/{chatroomId}/{username}/{messageId}")
215   void testInvalidUsernameException(@Autowired WebTestClient client)
216   {
217     // Given
218     UUID chatroomId = UUID.randomUUID();
219     String user = "Foo";
220     Long messageId = 66l;
221     Message.MessageKey key = Message.MessageKey.of(user, messageId);
222     String textMessage = "Hallo Welt";
223     ChatRoomData chatRoomData = new ChatRoomData(
224         Clock.systemDefaultZone(),
225         chatRoomService,
226         8);
227     when(chatHome.getChatRoomData(any(UUID.class)))
228         .thenReturn(Mono.just(chatRoomData));
229     when(chatRoomService.getMessage(any(Message.MessageKey.class)))
230         .thenReturn(Mono.empty());
231     // Needed for readable error-reports, in case of a bug that leads to according unwanted call
232     when(chatRoomService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
233         .thenReturn(Mono.just(mock(Message.class)));
234
235     // When
236     client
237         .put()
238         .uri(
239             "/{chatroomId}/{username}/{messageId}",
240             chatroomId,
241             user,
242             messageId)
243         .bodyValue(textMessage)
244         .accept(MediaType.APPLICATION_JSON)
245         .exchange()
246         // Then
247         .expectStatus().is4xxClientError()
248         .expectBody()
249         .jsonPath("$.type").isEqualTo("/problem/invalid-username")
250         .jsonPath("$.username").isEqualTo(user);
251     verify(chatRoomService, never()).persistMessage(eq(key), any(LocalDateTime.class), any(String.class));
252   }
253
254   @Test
255   @DisplayName("Assert expected problem-details for not owned shard on GET /{chatroomId}")
256   void testShardNotOwnedExceptionForGetChatroom(@Autowired WebTestClient client)
257   {
258     // Given
259     UUID chatroomId = UUID.randomUUID();
260     int shard = 666;
261     when(chatHome.getChatRoomInfo(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
262
263     // When
264     WebTestClient.ResponseSpec responseSpec = client
265         .get()
266         .uri("/{chatroomId}", chatroomId)
267         .accept(MediaType.APPLICATION_JSON)
268         .exchange();
269
270     // Then
271     assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
272   }
273
274   @Test
275   @DisplayName("Assert expected problem-details for not owned shard on GET /list/{chatroomId}")
276   void testShardNotOwnedExceptionForListChatroom(@Autowired WebTestClient client)
277   {
278     // Given
279     UUID chatroomId = UUID.randomUUID();
280     int shard = 666;
281     when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
282
283     // When
284     WebTestClient.ResponseSpec responseSpec = client
285         .get()
286         .uri("/{chatroomId}/list", chatroomId)
287         .accept(MediaType.APPLICATION_JSON)
288         .exchange();
289
290     // Then
291     assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
292   }
293
294   @Test
295   @DisplayName("Assert expected problem-details for not owned shard on PUT /put/{chatroomId}/{username}/{messageId}")
296   void testShardNotOwnedExceptionForPutMessage(@Autowired WebTestClient client)
297   {
298     // Given
299     UUID chatroomId = UUID.randomUUID();
300     String username = "foo";
301     Long messageId = 66l;
302     int shard = 666;
303     when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
304
305     // When
306     WebTestClient.ResponseSpec responseSpec = client
307         .put()
308         .uri(
309             "/{chatroomId}/{username}/{messageId}",
310             chatroomId,
311             username,
312             messageId)
313         .bodyValue("bar")
314         .accept(MediaType.APPLICATION_JSON)
315         .exchange();
316
317     // Then
318     assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
319   }
320
321   @Test
322   @DisplayName("Assert expected problem-details for not owned shard on GET /get/{chatroomId}/{username}/{messageId}")
323   void testShardNotOwnedExceptionForGetMessage(@Autowired WebTestClient client)
324   {
325     // Given
326     UUID chatroomId = UUID.randomUUID();
327     String username = "foo";
328     Long messageId = 66l;
329     int shard = 666;
330     when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
331
332     // When
333     WebTestClient.ResponseSpec responseSpec = client
334         .get()
335         .uri(
336             "/{chatroomId}/{username}/{messageId}",
337             chatroomId,
338             username,
339             messageId)
340         .accept(MediaType.APPLICATION_JSON)
341         .exchange();
342
343     // Then
344     assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
345   }
346
347   @Test
348   @DisplayName("Assert expected problem-details for not owned shard on GET /listen/{chatroomId}")
349   void testShardNotOwnedExceptionForListenChatroom(@Autowired WebTestClient client)
350   {
351     // Given
352     UUID chatroomId = UUID.randomUUID();
353     int shard = 666;
354     when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
355
356     // When
357     WebTestClient.ResponseSpec responseSpec = client
358         .get()
359         .uri("/{chatroomId}/listen", chatroomId)
360         // .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON) << TODO: Does not work!
361         .exchange();
362
363     // Then
364     assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
365   }
366
367   private void assertProblemDetailsForShardNotOwnedException(
368       WebTestClient.ResponseSpec responseSpec,
369       int shard)
370   {
371     responseSpec
372         .expectStatus().isNotFound()
373         .expectBody()
374         .jsonPath("$.type").isEqualTo("/problem/shard-not-owned")
375         .jsonPath("$.shard").isEqualTo(shard);
376   }
377 }