1 package de.juplo.kafka.chat.backend.api;
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;
16 import java.time.Clock;
17 import java.time.LocalDateTime;
18 import java.util.UUID;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.*;
24 @SpringBootTest(properties = {
25 "spring.main.allow-bean-definition-overriding=true",
27 @AutoConfigureWebTestClient
29 public class ChatBackendControllerTest
32 ChatBackendProperties properties;
37 ChatRoomService chatRoomService;
40 @DisplayName("Assert expected problem-details for unknown chatroom on GET /list/{chatroomId}")
41 void testUnknownChatroomExceptionForListChatroom(@Autowired WebTestClient client)
44 UUID chatroomId = UUID.randomUUID();
45 when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new UnknownChatroomException(chatroomId));
48 WebTestClient.ResponseSpec responseSpec = client
50 .uri("/{chatroomId}/list", chatroomId)
51 .accept(MediaType.APPLICATION_JSON)
55 assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
60 @DisplayName("Assert expected problem-details for unknown chatroom on GET /get/{chatroomId}")
61 void testUnknownChatroomExceptionForGetChatroom(@Autowired WebTestClient client)
64 UUID chatroomId = UUID.randomUUID();
65 when(chatHome.getChatRoomInfo(eq(chatroomId))).thenThrow(new UnknownChatroomException(chatroomId));
68 WebTestClient.ResponseSpec responseSpec = client
70 .uri("/{chatroomId}", chatroomId)
71 .accept(MediaType.APPLICATION_JSON)
75 assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
79 @DisplayName("Assert expected problem-details for unknown chatroom on PUT /put/{chatroomId}/{username}/{messageId}")
80 void testUnknownChatroomExceptionForPutMessage(@Autowired WebTestClient client)
83 UUID chatroomId = UUID.randomUUID();
84 String username = "foo";
86 when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new UnknownChatroomException(chatroomId));
89 WebTestClient.ResponseSpec responseSpec = client
92 "/{chatroomId}/{username}/{messageId}",
97 .accept(MediaType.APPLICATION_JSON)
101 assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
105 @DisplayName("Assert expected problem-details for unknown chatroom on GET /get/{chatroomId}/{username}/{messageId}")
106 void testUnknownChatroomExceptionForGetMessage(@Autowired WebTestClient client)
109 UUID chatroomId = UUID.randomUUID();
110 String username = "foo";
111 Long messageId = 66l;
112 when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new UnknownChatroomException(chatroomId));
115 WebTestClient.ResponseSpec responseSpec = client
118 "/{chatroomId}/{username}/{messageId}",
122 .accept(MediaType.APPLICATION_JSON)
126 assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
130 @DisplayName("Assert expected problem-details for unknown chatroom on GET /listen/{chatroomId}")
131 void testUnknownChatroomExceptionForListenChatroom(@Autowired WebTestClient client)
134 UUID chatroomId = UUID.randomUUID();
135 when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new UnknownChatroomException(chatroomId));
138 WebTestClient.ResponseSpec responseSpec = client
140 .uri("/{chatroomId}/listen", chatroomId)
141 // .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON) << TODO: Does not work!
145 assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
148 private void assertProblemDetailsForUnknownChatroomException(
149 WebTestClient.ResponseSpec responseSpec,
153 .expectStatus().isNotFound()
155 .jsonPath("$.type").isEqualTo("/problem/unknown-chatroom")
156 .jsonPath("$.chatroomId").isEqualTo(chatroomId.toString());
160 @DisplayName("Assert expected problem-details for message mutation on PUT /put/{chatroomId}/{username}/{messageId}")
161 void testMessageMutationException(@Autowired WebTestClient client)
164 UUID chatroomId = UUID.randomUUID();
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(),
177 when(chatHome.getChatRoomData(eq(chatroomId))).thenReturn(Mono.just(chatRoomData));
178 Message existingMessage = new Message(
180 serialNumberExistingMessage,
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)));
193 "/{chatroomId}/{username}/{messageId}",
197 .bodyValue(textMutatedMessage)
198 .accept(MediaType.APPLICATION_JSON)
201 .expectStatus().is4xxClientError()
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));
214 @DisplayName("Assert expected problem-details for invalid username on PUT /put/{chatroomId}/{username}/{messageId}")
215 void testInvalidUsernameException(@Autowired WebTestClient client)
218 UUID chatroomId = UUID.randomUUID();
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(),
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)));
239 "/{chatroomId}/{username}/{messageId}",
243 .bodyValue(textMessage)
244 .accept(MediaType.APPLICATION_JSON)
247 .expectStatus().is4xxClientError()
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));
255 @DisplayName("Assert expected problem-details for not owned shard on GET /{chatroomId}")
256 void testShardNotOwnedExceptionForGetChatroom(@Autowired WebTestClient client)
259 UUID chatroomId = UUID.randomUUID();
261 when(chatHome.getChatRoomInfo(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
264 WebTestClient.ResponseSpec responseSpec = client
266 .uri("/{chatroomId}", chatroomId)
267 .accept(MediaType.APPLICATION_JSON)
271 assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
275 @DisplayName("Assert expected problem-details for not owned shard on GET /list/{chatroomId}")
276 void testShardNotOwnedExceptionForListChatroom(@Autowired WebTestClient client)
279 UUID chatroomId = UUID.randomUUID();
281 when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
284 WebTestClient.ResponseSpec responseSpec = client
286 .uri("/{chatroomId}/list", chatroomId)
287 .accept(MediaType.APPLICATION_JSON)
291 assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
295 @DisplayName("Assert expected problem-details for not owned shard on PUT /put/{chatroomId}/{username}/{messageId}")
296 void testShardNotOwnedExceptionForPutMessage(@Autowired WebTestClient client)
299 UUID chatroomId = UUID.randomUUID();
300 String username = "foo";
301 Long messageId = 66l;
303 when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
306 WebTestClient.ResponseSpec responseSpec = client
309 "/{chatroomId}/{username}/{messageId}",
314 .accept(MediaType.APPLICATION_JSON)
318 assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
322 @DisplayName("Assert expected problem-details for not owned shard on GET /get/{chatroomId}/{username}/{messageId}")
323 void testShardNotOwnedExceptionForGetMessage(@Autowired WebTestClient client)
326 UUID chatroomId = UUID.randomUUID();
327 String username = "foo";
328 Long messageId = 66l;
330 when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
333 WebTestClient.ResponseSpec responseSpec = client
336 "/{chatroomId}/{username}/{messageId}",
340 .accept(MediaType.APPLICATION_JSON)
344 assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
348 @DisplayName("Assert expected problem-details for not owned shard on GET /listen/{chatroomId}")
349 void testShardNotOwnedExceptionForListenChatroom(@Autowired WebTestClient client)
352 UUID chatroomId = UUID.randomUUID();
354 when(chatHome.getChatRoomData(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
357 WebTestClient.ResponseSpec responseSpec = client
359 .uri("/{chatroomId}/listen", chatroomId)
360 // .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON) << TODO: Does not work!
364 assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
367 private void assertProblemDetailsForShardNotOwnedException(
368 WebTestClient.ResponseSpec responseSpec,
372 .expectStatus().isNotFound()
374 .jsonPath("$.type").isEqualTo("/problem/shard-not-owned")
375 .jsonPath("$.shard").isEqualTo(shard);