1 package de.juplo.kafka.chat.backend;
3 import com.fasterxml.jackson.core.type.TypeReference;
4 import com.fasterxml.jackson.databind.ObjectMapper;
5 import de.juplo.kafka.chat.backend.api.ChatRoomInfoTo;
6 import de.juplo.kafka.chat.backend.api.MessageTo;
7 import lombok.extern.slf4j.Slf4j;
8 import org.junit.jupiter.api.BeforeEach;
9 import org.junit.jupiter.api.DisplayName;
10 import org.junit.jupiter.api.Test;
11 import org.springframework.beans.factory.annotation.Autowired;
12 import org.springframework.beans.factory.annotation.Value;
13 import org.springframework.boot.test.web.server.LocalServerPort;
14 import org.springframework.core.io.Resource;
15 import org.springframework.http.MediaType;
16 import org.springframework.test.annotation.DirtiesContext;
17 import org.springframework.test.web.reactive.server.WebTestClient;
18 import org.testcontainers.shaded.org.awaitility.Awaitility;
19 import reactor.core.publisher.Flux;
21 import java.io.IOException;
22 import java.time.Duration;
23 import java.util.List;
24 import java.util.UUID;
25 import java.util.concurrent.atomic.AtomicBoolean;
26 import java.util.random.RandomGenerator;
28 import static org.assertj.core.api.Assertions.assertThat;
29 import static org.hamcrest.Matchers.endsWith;
34 public abstract class AbstractConfigurationIT
36 final static String EXISTING_CHATROOM = "5c73531c-6fc4-426c-adcb-afc5c140a0f7";
37 String NONEXISTENT_CHATROOM = "7f59ec77-832e-4a17-8d22-55ef46242c17";
43 WebTestClient webTestClient;
45 ObjectMapper objectMapper;
47 @Value("classpath:data/files/5c73531c-6fc4-426c-adcb-afc5c140a0f7.json")
48 Resource existingChatRoomRessource;
49 MessageTo[] expectedExistingMessages;
53 void waitForApp() throws IOException
55 expectedExistingMessages = objectMapper
57 existingChatRoomRessource.getInputStream(),
58 new TypeReference<List<MessageTo>>() {})
59 .toArray(size -> new MessageTo[size]);
63 .atMost(Duration.ofSeconds(15))
69 "http://localhost:{port}/actuator/health",
72 .expectStatus().isOk()
73 .expectBody().jsonPath("$.status").isEqualTo("UP");
78 @DisplayName("Restored chat-rooms can be listed")
79 void testRestoredChatRoomsCanBeListed()
83 .atMost(Duration.ofSeconds(15))
86 AtomicBoolean existingChatRoomFound = new AtomicBoolean(false);
90 "http://localhost:{port}/list",
92 .accept(MediaType.APPLICATION_JSON)
94 .expectStatus().isOk()
95 .returnResult(ChatRoomInfoTo.class)
98 .forEach(chatRoomInfoTo ->
100 log.debug("Inspecting chat-room {}", chatRoomInfoTo);
101 if (chatRoomInfoTo.getId().equals(UUID.fromString(EXISTING_CHATROOM)))
103 log.debug("Found existing chat-room {}", chatRoomInfoTo);
104 existingChatRoomFound.set(true);
105 assertThat(chatRoomInfoTo.getName().equals("FOO"));
108 assertThat(existingChatRoomFound.get()).isTrue();
113 @DisplayName("Details as expected for restored chat-room")
114 void testRestoredChatRoomHasExpectedDetails()
118 .atMost(Duration.ofSeconds(15))
124 "http://localhost:{port}/{chatRoomId}",
127 .accept(MediaType.APPLICATION_JSON)
129 .expectStatus().isOk()
130 .expectBody().jsonPath("$.name").isEqualTo("FOO");
135 @DisplayName("Restored message from Ute has expected Text")
136 void testRestoredMessageForUteHasExpectedText()
140 .atMost(Duration.ofSeconds(15))
146 "http://localhost:{port}/{chatRoomId}/ute/1",
149 .accept(MediaType.APPLICATION_JSON)
151 .expectStatus().isOk()
152 .expectBody().jsonPath("$.text").isEqualTo("Ich bin Ute...");
157 @DisplayName("Restored message from Peter has expected Text")
158 void testRestoredMessageForPeterHasExpectedText()
162 .atMost(Duration.ofSeconds(15))
168 "http://localhost:{port}/{chatRoomId}/peter/1",
171 .accept(MediaType.APPLICATION_JSON)
173 .expectStatus().isOk()
174 .expectBody().jsonPath("$.text").isEqualTo("Hallo, ich heiße Peter!");
179 @DisplayName("A PUT-message for a non-existent chat-room yields 404 NOT FOUND")
180 void testNotFoundForPutMessageToNonExistentChatRoom()
184 .atMost(Duration.ofSeconds(15))
190 "http://localhost:{port}/{chatRoomId}/otto/66",
192 NONEXISTENT_CHATROOM)
193 .contentType(MediaType.TEXT_PLAIN)
194 .accept(MediaType.APPLICATION_JSON)
195 .bodyValue("The devil rules route 66")
197 .expectStatus().isNotFound()
199 .jsonPath("$.type").value(endsWith("/problem/unknown-chatroom"))
200 .jsonPath("$.chatroomId").isEqualTo(NONEXISTENT_CHATROOM);
205 @DisplayName("A message can be put into a newly created chat-room")
206 void testPutMessageInNewChatRoom() throws IOException
208 ChatRoomInfoTo chatRoomInfo;
211 // The first request creates a new chat-room
212 // It must be repeated, until a chat-room was created,
213 // that is owned by the instance
214 chatRoomInfo = webTestClient
216 .uri("http://localhost:{port}/create", port)
217 .contentType(MediaType.TEXT_PLAIN)
219 .accept(MediaType.APPLICATION_JSON)
221 .returnResult(ChatRoomInfoTo.class)
226 while(!(chatRoomInfo.getShard() == null || chatRoomInfo.getShard().intValue() == 2));
228 UUID chatRoomId = chatRoomInfo.getId();
232 .atMost(Duration.ofSeconds(15))
238 "http://localhost:{port}/{chatRoomId}/nerd/7",
241 .contentType(MediaType.TEXT_PLAIN)
242 .accept(MediaType.APPLICATION_JSON)
243 .bodyValue("Hello world!")
245 .expectStatus().isOk()
247 .jsonPath("$.id").isEqualTo(Integer.valueOf(7))
248 .jsonPath("$.user").isEqualTo("nerd")
249 .jsonPath("$.text").isEqualTo("Hello world!");
254 @DisplayName("Only newly send messages can be seen, when listening to restored chat-room")
255 void testListenToRestoredChatRoomYieldsOnlyNewlyAddedMessages()
257 MessageTo sentMessage = webTestClient
260 "http://localhost:{port}/{chatRoomId}/nerd/{messageId}",
263 RandomGenerator.getDefault().nextInt())
264 .contentType(MediaType.TEXT_PLAIN)
265 .accept(MediaType.APPLICATION_JSON)
266 .bodyValue("Hello world!")
270 .returnResult(MessageTo.class)
275 Flux<MessageTo> result = webTestClient
278 "http://localhost:{port}/{chatRoomId}/listen",
281 .accept(MediaType.TEXT_EVENT_STREAM)
283 .expectStatus().isOk()
284 .returnResult(MessageTo.class)
287 assertThat(result.next().block()).isEqualTo(sentMessage);