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 de.juplo.kafka.chat.backend.persistence.inmemory.InMemoryChatHomeService;
6 import lombok.extern.slf4j.Slf4j;
7 import org.junit.jupiter.api.DisplayName;
8 import org.junit.jupiter.api.Test;
9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
11 import org.springframework.boot.test.context.SpringBootTest;
12 import org.springframework.boot.test.context.TestConfiguration;
13 import org.springframework.boot.test.mock.mockito.MockBean;
14 import org.springframework.context.annotation.Bean;
15 import org.springframework.http.MediaType;
16 import org.springframework.test.web.reactive.server.WebTestClient;
17 import reactor.core.publisher.Mono;
19 import java.time.Clock;
20 import java.time.LocalDateTime;
21 import java.util.Arrays;
22 import java.util.UUID;
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.Mockito.*;
28 @SpringBootTest(properties = {
29 "spring.main.allow-bean-definition-overriding=true",
30 "chat.backend.inmemory.owned-shards=0,1,2,3,4,5,6,7,8,9" })
31 @AutoConfigureWebTestClient
33 public class ChatBackendControllerTest
36 InMemoryChatHomeService chatHomeService;
38 ChatRoomService chatRoomService;
41 @DisplayName("Assert expected problem-details for unknown chatroom on GET /list/{chatroomId}")
42 void testUnknownChatroomExceptionForListChatroom(@Autowired WebTestClient client)
45 UUID chatroomId = UUID.randomUUID();
46 when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
49 WebTestClient.ResponseSpec responseSpec = client
51 .uri("/{chatroomId}/list", chatroomId)
52 .accept(MediaType.APPLICATION_JSON)
56 assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
61 @DisplayName("Assert expected problem-details for unknown chatroom on GET /get/{chatroomId}")
62 void testUnknownChatroomExceptionForGetChatroom(@Autowired WebTestClient client)
65 UUID chatroomId = UUID.randomUUID();
66 when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
69 WebTestClient.ResponseSpec responseSpec = client
71 .uri("/{chatroomId}", chatroomId)
72 .accept(MediaType.APPLICATION_JSON)
76 assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
80 @DisplayName("Assert expected problem-details for unknown chatroom on PUT /put/{chatroomId}/{username}/{messageId}")
81 void testUnknownChatroomExceptionForPutMessage(@Autowired WebTestClient client)
84 UUID chatroomId = UUID.randomUUID();
85 String username = "foo";
87 when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
90 WebTestClient.ResponseSpec responseSpec = client
93 "/{chatroomId}/{username}/{messageId}",
98 .accept(MediaType.APPLICATION_JSON)
102 assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
106 @DisplayName("Assert expected problem-details for unknown chatroom on GET /get/{chatroomId}/{username}/{messageId}")
107 void testUnknownChatroomExceptionForGetMessage(@Autowired WebTestClient client)
110 UUID chatroomId = UUID.randomUUID();
111 String username = "foo";
112 Long messageId = 66l;
113 when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
116 WebTestClient.ResponseSpec responseSpec = client
119 "/{chatroomId}/{username}/{messageId}",
123 .accept(MediaType.APPLICATION_JSON)
127 assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
131 @DisplayName("Assert expected problem-details for unknown chatroom on GET /listen/{chatroomId}")
132 void testUnknownChatroomExceptionForListenChatroom(@Autowired WebTestClient client)
135 UUID chatroomId = UUID.randomUUID();
136 when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
139 WebTestClient.ResponseSpec responseSpec = client
141 .uri("/{chatroomId}/listen", chatroomId)
142 // .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON) << TODO: Does not work!
146 assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
149 private void assertProblemDetailsForUnknownChatroomException(
150 WebTestClient.ResponseSpec responseSpec,
154 .expectStatus().isNotFound()
156 .jsonPath("$.type").isEqualTo("/problem/unknown-chatroom")
157 .jsonPath("$.chatroomId").isEqualTo(chatroomId.toString());
161 @DisplayName("Assert expected problem-details for message mutation on PUT /put/{chatroomId}/{username}/{messageId}")
162 void testMessageMutationException(@Autowired WebTestClient client) throws Exception
165 UUID chatroomId = UUID.randomUUID();
167 Long messageId = 66l;
168 Message.MessageKey key = Message.MessageKey.of(user, messageId);
169 Long serialNumberExistingMessage = 0l;
170 String timeExistingMessageAsString = "2023-01-09T20:44:57.389665447";
171 LocalDateTime timeExistingMessage = LocalDateTime.parse(timeExistingMessageAsString);
172 String textExistingMessage = "Existing";
173 String textMutatedMessage = "Mutated!";
174 ChatRoom chatRoom = new ChatRoom(
178 Clock.systemDefaultZone(),
180 when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.just(chatRoom));
181 Message existingMessage = new Message(
183 serialNumberExistingMessage,
185 textExistingMessage);
186 when(chatRoomService.getMessage(any(Message.MessageKey.class)))
187 .thenReturn(Mono.just(existingMessage));
188 // Needed for readable error-reports, in case of a bug that leads to according unwanted call
189 when(chatRoomService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
190 .thenReturn(mock(Message.class));
196 "/{chatroomId}/{username}/{messageId}",
200 .bodyValue(textMutatedMessage)
201 .accept(MediaType.APPLICATION_JSON)
204 .expectStatus().is4xxClientError()
206 .jsonPath("$.type").isEqualTo("/problem/message-mutation")
207 .jsonPath("$.existingMessage.id").isEqualTo(messageId)
208 .jsonPath("$.existingMessage.serial").isEqualTo(serialNumberExistingMessage)
209 .jsonPath("$.existingMessage.time").isEqualTo(timeExistingMessageAsString)
210 .jsonPath("$.existingMessage.user").isEqualTo(user)
211 .jsonPath("$.existingMessage.text").isEqualTo(textExistingMessage)
212 .jsonPath("$.mutatedText").isEqualTo(textMutatedMessage);
213 verify(chatRoomService, never()).persistMessage(eq(key), any(LocalDateTime.class), any(String.class));
217 @DisplayName("Assert expected problem-details for invalid username on PUT /put/{chatroomId}/{username}/{messageId}")
218 void testInvalidUsernameException(@Autowired WebTestClient client) throws Exception
221 UUID chatroomId = UUID.randomUUID();
223 Long messageId = 66l;
224 Message.MessageKey key = Message.MessageKey.of(user, messageId);
225 String textMessage = "Hallo Welt";
226 ChatRoom chatRoom = new ChatRoom(
230 Clock.systemDefaultZone(),
232 when(chatHomeService.getChatRoom(anyInt(), any(UUID.class)))
233 .thenReturn(Mono.just(chatRoom));
234 when(chatRoomService.getMessage(any(Message.MessageKey.class)))
235 .thenReturn(Mono.empty());
236 // Needed for readable error-reports, in case of a bug that leads to according unwanted call
237 when(chatRoomService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
238 .thenReturn(mock(Message.class));
244 "/{chatroomId}/{username}/{messageId}",
248 .bodyValue(textMessage)
249 .accept(MediaType.APPLICATION_JSON)
252 .expectStatus().is4xxClientError()
254 .jsonPath("$.type").isEqualTo("/problem/invalid-username")
255 .jsonPath("$.username").isEqualTo(user);
256 verify(chatRoomService, never()).persistMessage(eq(key), any(LocalDateTime.class), any(String.class));
263 ChatHome[] chatHomes(
264 ChatBackendProperties properties,
265 InMemoryChatHomeService service)
267 SimpleChatHome[] chatHomes = new SimpleChatHome[properties.getInmemory().getNumShards()];
269 .stream(properties.getInmemory().getOwnedShards())
270 .forEach(i -> chatHomes[i] = new SimpleChatHome(service, i));