refactor: Made `SimpleChatHome` an implementation of `ChatHome`
[demos/kafka/chat] / src / test / java / de / juplo / kafka / chat / backend / api / ChatBackendControllerTest.java
index 08361dd..b1c80a9 100644 (file)
@@ -1,47 +1,54 @@
 package de.juplo.kafka.chat.backend.api;
 
+import de.juplo.kafka.chat.backend.ChatBackendProperties;
 import de.juplo.kafka.chat.backend.domain.*;
+import de.juplo.kafka.chat.backend.persistence.inmemory.InMemoryChatHomeService;
 import lombok.extern.slf4j.Slf4j;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.TestConfiguration;
 import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Bean;
 import org.springframework.http.MediaType;
 import org.springframework.test.web.reactive.server.WebTestClient;
+import reactor.core.publisher.Mono;
 
+import java.time.Clock;
 import java.time.LocalDateTime;
-import java.util.Optional;
+import java.util.Arrays;
 import java.util.UUID;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 
-@SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true")
+@SpringBootTest(properties = {
+    "spring.main.allow-bean-definition-overriding=true",
+    "chat.backend.inmemory.owned-shards=0,1,2,3,4,5,6,7,8,9" })
 @AutoConfigureWebTestClient
 @Slf4j
 public class ChatBackendControllerTest
 {
   @MockBean
-  ChatHome chatHome;
+  InMemoryChatHomeService chatHomeService;
+  @MockBean
+  ChatRoomService chatRoomService;
 
-  @Disabled
   @Test
   @DisplayName("Assert expected problem-details for unknown chatroom on GET /list/{chatroomId}")
   void testUnknownChatroomExceptionForListChatroom(@Autowired WebTestClient client)
   {
     // Given
     UUID chatroomId = UUID.randomUUID();
-    when(chatHome.getChatroom(any(UUID.class))).thenReturn(Optional.empty());
+    when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
 
     // When
     WebTestClient.ResponseSpec responseSpec = client
         .get()
-        .uri("/list/{chatroomId}", chatroomId)
+        .uri("/{chatroomId}/list", chatroomId)
         .accept(MediaType.APPLICATION_JSON)
         .exchange();
 
@@ -50,19 +57,18 @@ public class ChatBackendControllerTest
   }
 
 
-  @Disabled
   @Test
   @DisplayName("Assert expected problem-details for unknown chatroom on GET /get/{chatroomId}")
   void testUnknownChatroomExceptionForGetChatroom(@Autowired WebTestClient client)
   {
     // Given
     UUID chatroomId = UUID.randomUUID();
-    when(chatHome.getChatroom(any(UUID.class))).thenReturn(Optional.empty());
+    when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
 
     // When
     WebTestClient.ResponseSpec responseSpec = client
         .get()
-        .uri("/get/{chatroomId}", chatroomId)
+        .uri("/{chatroomId}", chatroomId)
         .accept(MediaType.APPLICATION_JSON)
         .exchange();
 
@@ -78,13 +84,13 @@ public class ChatBackendControllerTest
     UUID chatroomId = UUID.randomUUID();
     String username = "foo";
     Long messageId = 66l;
-    when(chatHome.getChatroom(any(UUID.class))).thenReturn(Optional.empty());
+    when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
 
     // When
     WebTestClient.ResponseSpec responseSpec = client
         .put()
         .uri(
-            "/put/{chatroomId}/{username}/{messageId}",
+            "/{chatroomId}/{username}/{messageId}",
             chatroomId,
             username,
             messageId)
@@ -104,13 +110,13 @@ public class ChatBackendControllerTest
     UUID chatroomId = UUID.randomUUID();
     String username = "foo";
     Long messageId = 66l;
-    when(chatHome.getChatroom(any(UUID.class))).thenReturn(Optional.empty());
+    when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
 
     // When
     WebTestClient.ResponseSpec responseSpec = client
         .get()
         .uri(
-            "/get/{chatroomId}/{username}/{messageId}",
+            "/{chatroomId}/{username}/{messageId}",
             chatroomId,
             username,
             messageId)
@@ -127,13 +133,13 @@ public class ChatBackendControllerTest
   {
     // Given
     UUID chatroomId = UUID.randomUUID();
-    when(chatHome.getChatroom(any(UUID.class))).thenReturn(Optional.empty());
+    when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.empty());
 
     // When
     WebTestClient.ResponseSpec responseSpec = client
         .get()
-        .uri("/listen/{chatroomId}", chatroomId)
-        .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON)
+        .uri("/{chatroomId}/listen", chatroomId)
+        // .accept(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_JSON) << TODO: Does not work!
         .exchange();
 
     // Then
@@ -157,34 +163,112 @@ public class ChatBackendControllerTest
   {
     // Given
     UUID chatroomId = UUID.randomUUID();
-    String username = "foo";
+    String user = "foo";
     Long messageId = 66l;
-    ChatRoom chatRoom = mock(ChatRoom.class);
-    when(chatHome.getChatroom(any(UUID.class)))
-        .thenReturn(Optional.of(chatRoom));
-    Message.MessageKey key = Message.MessageKey.of("foo", 1l);
-    LocalDateTime timestamp = LocalDateTime.now();
-    Message mutated = new Message(key, 0l, timestamp, "Mutated!");
-    Message existing = new Message(key, 0l, timestamp, "Existing");
-    when(chatRoom.addMessage(any(Long.class), any(String.class), any(String.class)))
-        .thenThrow(new MessageMutationException(mutated, existing));
+    Message.MessageKey key = Message.MessageKey.of(user, messageId);
+    Long serialNumberExistingMessage = 0l;
+    String timeExistingMessageAsString = "2023-01-09T20:44:57.389665447";
+    LocalDateTime timeExistingMessage = LocalDateTime.parse(timeExistingMessageAsString);
+    String textExistingMessage = "Existing";
+    String textMutatedMessage = "Mutated!";
+    ChatRoom chatRoom = new ChatRoom(
+        chatroomId,
+        "Test-ChatRoom",
+        0,
+        Clock.systemDefaultZone(),
+        chatRoomService, 8);
+    when(chatHomeService.getChatRoom(anyInt(), any(UUID.class))).thenReturn(Mono.just(chatRoom));
+    Message existingMessage = new Message(
+        key,
+        serialNumberExistingMessage,
+        timeExistingMessage,
+        textExistingMessage);
+    when(chatRoomService.getMessage(any(Message.MessageKey.class)))
+        .thenReturn(Mono.just(existingMessage));
+    // Needed for readable error-reports, in case of a bug that leads to according unwanted call
+    when(chatRoomService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
+        .thenReturn(mock(Message.class));
 
     // When
     client
         .put()
         .uri(
-            "/put/{chatroomId}/{username}/{messageId}",
+            "/{chatroomId}/{username}/{messageId}",
             chatroomId,
-            username,
+            user,
             messageId)
-        .bodyValue("bar")
+        .bodyValue(textMutatedMessage)
         .accept(MediaType.APPLICATION_JSON)
         .exchange()
         // Then
         .expectStatus().is4xxClientError()
         .expectBody()
         .jsonPath("$.type").isEqualTo("/problem/message-mutation")
-        .jsonPath("$.mutatedMessage.text").isEqualTo("Mutated!")
-        .jsonPath("$.existingMessage.text").isEqualTo("Existing");
+        .jsonPath("$.existingMessage.id").isEqualTo(messageId)
+        .jsonPath("$.existingMessage.serial").isEqualTo(serialNumberExistingMessage)
+        .jsonPath("$.existingMessage.time").isEqualTo(timeExistingMessageAsString)
+        .jsonPath("$.existingMessage.user").isEqualTo(user)
+        .jsonPath("$.existingMessage.text").isEqualTo(textExistingMessage)
+        .jsonPath("$.mutatedText").isEqualTo(textMutatedMessage);
+    verify(chatRoomService, never()).persistMessage(eq(key), any(LocalDateTime.class), any(String.class));
+  }
+
+  @Test
+  @DisplayName("Assert expected problem-details for invalid username on PUT /put/{chatroomId}/{username}/{messageId}")
+  void testInvalidUsernameException(@Autowired WebTestClient client) throws Exception
+  {
+    // Given
+    UUID chatroomId = UUID.randomUUID();
+    String user = "Foo";
+    Long messageId = 66l;
+    Message.MessageKey key = Message.MessageKey.of(user, messageId);
+    String textMessage = "Hallo Welt";
+    ChatRoom chatRoom = new ChatRoom(
+        chatroomId,
+        "Test-ChatRoom",
+        0,
+        Clock.systemDefaultZone(),
+        chatRoomService, 8);
+    when(chatHomeService.getChatRoom(anyInt(), any(UUID.class)))
+        .thenReturn(Mono.just(chatRoom));
+    when(chatRoomService.getMessage(any(Message.MessageKey.class)))
+        .thenReturn(Mono.empty());
+    // Needed for readable error-reports, in case of a bug that leads to according unwanted call
+    when(chatRoomService.persistMessage(any(Message.MessageKey.class), any(LocalDateTime.class), any(String.class)))
+        .thenReturn(mock(Message.class));
+
+    // When
+    client
+        .put()
+        .uri(
+            "/{chatroomId}/{username}/{messageId}",
+            chatroomId,
+            user,
+            messageId)
+        .bodyValue(textMessage)
+        .accept(MediaType.APPLICATION_JSON)
+        .exchange()
+        // Then
+        .expectStatus().is4xxClientError()
+        .expectBody()
+        .jsonPath("$.type").isEqualTo("/problem/invalid-username")
+        .jsonPath("$.username").isEqualTo(user);
+    verify(chatRoomService, never()).persistMessage(eq(key), any(LocalDateTime.class), any(String.class));
+  }
+
+  @TestConfiguration
+  static class Config
+  {
+    @Bean
+    ChatHome[] chatHomes(
+        ChatBackendProperties properties,
+        InMemoryChatHomeService service)
+    {
+      SimpleChatHome[] chatHomes = new SimpleChatHome[properties.getInmemory().getNumShards()];
+      Arrays
+          .stream(properties.getInmemory().getOwnedShards())
+          .forEach(i -> chatHomes[i] = new SimpleChatHome(service, i));
+      return chatHomes;
+    }
   }
 }