refactor: Made `SimpleChatHome` an implementation of `ChatHome`
[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.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;
18
19 import java.time.Clock;
20 import java.time.LocalDateTime;
21 import java.util.Arrays;
22 import java.util.UUID;
23
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.Mockito.*;
26
27
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
32 @Slf4j
33 public class ChatBackendControllerTest
34 {
35   @MockBean
36   InMemoryChatHomeService chatHomeService;
37   @MockBean
38   ChatRoomService chatRoomService;
39
40   @Test
41   @DisplayName("Assert expected problem-details for unknown chatroom on GET /list/{chatroomId}")
42   void testUnknownChatroomExceptionForListChatroom(@Autowired WebTestClient client)
43   {
44     // Given
45     UUID chatroomId = UUID.randomUUID();
46     when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
47
48     // When
49     WebTestClient.ResponseSpec responseSpec = client
50         .get()
51         .uri("/{chatroomId}/list", chatroomId)
52         .accept(MediaType.APPLICATION_JSON)
53         .exchange();
54
55     // Then
56     assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
57   }
58
59
60   @Test
61   @DisplayName("Assert expected problem-details for unknown chatroom on GET /get/{chatroomId}")
62   void testUnknownChatroomExceptionForGetChatroom(@Autowired WebTestClient client)
63   {
64     // Given
65     UUID chatroomId = UUID.randomUUID();
66     when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
67
68     // When
69     WebTestClient.ResponseSpec responseSpec = client
70         .get()
71         .uri("/{chatroomId}", chatroomId)
72         .accept(MediaType.APPLICATION_JSON)
73         .exchange();
74
75     // Then
76     assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
77   }
78
79   @Test
80   @DisplayName("Assert expected problem-details for unknown chatroom on PUT /put/{chatroomId}/{username}/{messageId}")
81   void testUnknownChatroomExceptionForPutMessage(@Autowired WebTestClient client)
82   {
83     // Given
84     UUID chatroomId = UUID.randomUUID();
85     String username = "foo";
86     Long messageId = 66l;
87     when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
88
89     // When
90     WebTestClient.ResponseSpec responseSpec = client
91         .put()
92         .uri(
93             "/{chatroomId}/{username}/{messageId}",
94             chatroomId,
95             username,
96             messageId)
97         .bodyValue("bar")
98         .accept(MediaType.APPLICATION_JSON)
99         .exchange();
100
101     // Then
102     assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
103   }
104
105   @Test
106   @DisplayName("Assert expected problem-details for unknown chatroom on GET /get/{chatroomId}/{username}/{messageId}")
107   void testUnknownChatroomExceptionForGetMessage(@Autowired WebTestClient client)
108   {
109     // Given
110     UUID chatroomId = UUID.randomUUID();
111     String username = "foo";
112     Long messageId = 66l;
113     when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
114
115     // When
116     WebTestClient.ResponseSpec responseSpec = client
117         .get()
118         .uri(
119             "/{chatroomId}/{username}/{messageId}",
120             chatroomId,
121             username,
122             messageId)
123         .accept(MediaType.APPLICATION_JSON)
124         .exchange();
125
126     // Then
127     assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
128   }
129
130   @Test
131   @DisplayName("Assert expected problem-details for unknown chatroom on GET /listen/{chatroomId}")
132   void testUnknownChatroomExceptionForListenChatroom(@Autowired WebTestClient client)
133   {
134     // Given
135     UUID chatroomId = UUID.randomUUID();
136     when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
137
138     // When
139     WebTestClient.ResponseSpec responseSpec = client
140         .get()
141         .uri("/{chatroomId}/listen", chatroomId)
142         // .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON) << TODO: Does not work!
143         .exchange();
144
145     // Then
146     assertProblemDetailsForUnknownChatroomException(responseSpec, chatroomId);
147   }
148
149   private void assertProblemDetailsForUnknownChatroomException(
150       WebTestClient.ResponseSpec responseSpec,
151       UUID chatroomId)
152   {
153     responseSpec
154         .expectStatus().isNotFound()
155         .expectBody()
156         .jsonPath("$.type").isEqualTo("/problem/unknown-chatroom")
157         .jsonPath("$.chatroomId").isEqualTo(chatroomId.toString());
158   }
159
160   @Test
161   @DisplayName("Assert expected problem-details for message mutation on PUT /put/{chatroomId}/{username}/{messageId}")
162   void testMessageMutationException(@Autowired WebTestClient client) throws Exception
163   {
164     // Given
165     UUID chatroomId = UUID.randomUUID();
166     String user = "foo";
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(
175         chatroomId,
176         "Test-ChatRoom",
177         0,
178         Clock.systemDefaultZone(),
179         chatRoomService, 8);
180     when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.just(chatRoom));
181     Message existingMessage = new Message(
182         key,
183         serialNumberExistingMessage,
184         timeExistingMessage,
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));
191
192     // When
193     client
194         .put()
195         .uri(
196             "/{chatroomId}/{username}/{messageId}",
197             chatroomId,
198             user,
199             messageId)
200         .bodyValue(textMutatedMessage)
201         .accept(MediaType.APPLICATION_JSON)
202         .exchange()
203         // Then
204         .expectStatus().is4xxClientError()
205         .expectBody()
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));
214   }
215
216   @Test
217   @DisplayName("Assert expected problem-details for invalid username on PUT /put/{chatroomId}/{username}/{messageId}")
218   void testInvalidUsernameException(@Autowired WebTestClient client) throws Exception
219   {
220     // Given
221     UUID chatroomId = UUID.randomUUID();
222     String user = "Foo";
223     Long messageId = 66l;
224     Message.MessageKey key = Message.MessageKey.of(user, messageId);
225     String textMessage = "Hallo Welt";
226     ChatRoom chatRoom = new ChatRoom(
227         chatroomId,
228         "Test-ChatRoom",
229         0,
230         Clock.systemDefaultZone(),
231         chatRoomService, 8);
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));
239
240     // When
241     client
242         .put()
243         .uri(
244             "/{chatroomId}/{username}/{messageId}",
245             chatroomId,
246             user,
247             messageId)
248         .bodyValue(textMessage)
249         .accept(MediaType.APPLICATION_JSON)
250         .exchange()
251         // Then
252         .expectStatus().is4xxClientError()
253         .expectBody()
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));
257   }
258
259   @TestConfiguration
260   static class Config
261   {
262     @Bean
263     ChatHome[] chatHomes(
264         ChatBackendProperties properties,
265         InMemoryChatHomeService service)
266     {
267       SimpleChatHome[] chatHomes = new SimpleChatHome[properties.getInmemory().getNumShards()];
268       Arrays
269           .stream(properties.getInmemory().getOwnedShards())
270           .forEach(i -> chatHomes[i] = new SimpleChatHome(service, i));
271       return chatHomes;
272     }
273   }
274 }