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