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.getChatRoom(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.getChatRoom(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.getChatRoom(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.getChatRoom(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.getChatRoom(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 ChatRoom chatRoom = new ChatRoom(
177 Clock.systemDefaultZone(),
179 when(chatHome.getChatRoom(eq(chatroomId))).thenReturn(Mono.just(chatRoom));
180 Message existingMessage = new Message(
182 serialNumberExistingMessage,
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)));
195 "/{chatroomId}/{username}/{messageId}",
199 .bodyValue(textMutatedMessage)
200 .accept(MediaType.APPLICATION_JSON)
203 .expectStatus().is4xxClientError()
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));
216 @DisplayName("Assert expected problem-details for invalid username on PUT /put/{chatroomId}/{username}/{messageId}")
217 void testInvalidUsernameException(@Autowired WebTestClient client)
220 UUID chatroomId = UUID.randomUUID();
222 Long messageId = 66l;
223 Message.MessageKey key = Message.MessageKey.of(user, messageId);
224 String textMessage = "Hallo Welt";
225 ChatRoom chatRoom = new ChatRoom(
229 Clock.systemDefaultZone(),
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)));
243 "/{chatroomId}/{username}/{messageId}",
247 .bodyValue(textMessage)
248 .accept(MediaType.APPLICATION_JSON)
251 .expectStatus().is4xxClientError()
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));
259 @DisplayName("Assert expected problem-details for not owned shard on GET /{chatroomId}")
260 void testShardNotOwnedExceptionForGetChatroom(@Autowired WebTestClient client)
263 UUID chatroomId = UUID.randomUUID();
265 when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
268 WebTestClient.ResponseSpec responseSpec = client
270 .uri("/{chatroomId}", chatroomId)
271 .accept(MediaType.APPLICATION_JSON)
275 assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
279 @DisplayName("Assert expected problem-details for not owned shard on GET /list/{chatroomId}")
280 void testShardNotOwnedExceptionForListChatroom(@Autowired WebTestClient client)
283 UUID chatroomId = UUID.randomUUID();
285 when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
288 WebTestClient.ResponseSpec responseSpec = client
290 .uri("/{chatroomId}/list", chatroomId)
291 .accept(MediaType.APPLICATION_JSON)
295 assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
299 @DisplayName("Assert expected problem-details for not owned shard on PUT /put/{chatroomId}/{username}/{messageId}")
300 void testShardNotOwnedExceptionForPutMessage(@Autowired WebTestClient client)
303 UUID chatroomId = UUID.randomUUID();
304 String username = "foo";
305 Long messageId = 66l;
307 when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
310 WebTestClient.ResponseSpec responseSpec = client
313 "/{chatroomId}/{username}/{messageId}",
318 .accept(MediaType.APPLICATION_JSON)
322 assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
326 @DisplayName("Assert expected problem-details for not owned shard on GET /get/{chatroomId}/{username}/{messageId}")
327 void testShardNotOwnedExceptionForGetMessage(@Autowired WebTestClient client)
330 UUID chatroomId = UUID.randomUUID();
331 String username = "foo";
332 Long messageId = 66l;
334 when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
337 WebTestClient.ResponseSpec responseSpec = client
340 "/{chatroomId}/{username}/{messageId}",
344 .accept(MediaType.APPLICATION_JSON)
348 assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
352 @DisplayName("Assert expected problem-details for not owned shard on GET /listen/{chatroomId}")
353 void testShardNotOwnedExceptionForListenChatroom(@Autowired WebTestClient client)
356 UUID chatroomId = UUID.randomUUID();
358 when(chatHome.getChatRoom(eq(chatroomId))).thenThrow(new ShardNotOwnedException(shard));
361 WebTestClient.ResponseSpec responseSpec = client
363 .uri("/{chatroomId}/listen", chatroomId)
364 // .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON) << TODO: Does not work!
368 assertProblemDetailsForShardNotOwnedException(responseSpec, shard);
371 private void assertProblemDetailsForShardNotOwnedException(
372 WebTestClient.ResponseSpec responseSpec,
376 .expectStatus().isNotFound()
378 .jsonPath("$.type").isEqualTo("/problem/shard-not-owned")
379 .jsonPath("$.shard").isEqualTo(shard);