From: Kai Moritz Date: Sun, 24 Mar 2024 10:35:07 +0000 (+0100) Subject: test: RED - Added IT for application-startup, if no data is available X-Git-Url: http://juplo.de/gitweb/?a=commitdiff_plain;h=c6e1cf60f34e78e8467b2d2f37d0e40f05596534;p=demos%2Fkafka%2Fchat test: RED - Added IT for application-startup, if no data is available --- diff --git a/src/test/java/de/juplo/kafka/chat/backend/AbstractApplicationStartupIT.java b/src/test/java/de/juplo/kafka/chat/backend/AbstractApplicationStartupIT.java new file mode 100644 index 00000000..e9d78f8b --- /dev/null +++ b/src/test/java/de/juplo/kafka/chat/backend/AbstractApplicationStartupIT.java @@ -0,0 +1,66 @@ +package de.juplo.kafka.chat.backend; + +import de.juplo.kafka.chat.backend.api.ChatRoomInfoTo; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.testcontainers.shaded.org.awaitility.Awaitility; +import reactor.core.publisher.Flux; + +import java.io.IOException; +import java.time.Duration; + +import static pl.rzrz.assertj.reactor.Assertions.assertThat; + + +@Slf4j +public abstract class AbstractApplicationStartupIT +{ + @Autowired + WebTestClient webTestClient; + + + @Test + @DisplayName("Applications starts when no data is available (fresh install)") + void testStartup() throws IOException + { + Awaitility + .await() + .atMost(Duration.ofSeconds(15)) + .untilAsserted(() -> webTestClient + .get() + .uri("/actuator/health") + .exchange() + .expectStatus().isOk() + .expectBody().jsonPath("$.status").isEqualTo("UP")); + } + + @Test + @DisplayName("Chat-rooms can be listed and returns an empty list") + void testListChatRooms() throws IOException + { + Awaitility + .await() + .atMost(Duration.ofSeconds(15)) + .untilAsserted(() -> + { + Flux result = webTestClient + .get() + .uri("/list") + .accept(MediaType.APPLICATION_JSON) + .exchange() + .expectStatus().isOk() + .returnResult(ChatRoomInfoTo.class) + .getResponseBody() + .doOnNext(chatRoomInfoTo -> + { + log.debug("Found chat-room {}", chatRoomInfoTo); + }); + + assertThat(result).emitsExactly(); + }); + } +} diff --git a/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithFilesAndShardingApplicationStartupIT.java b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithFilesAndShardingApplicationStartupIT.java new file mode 100644 index 00000000..3b8b0913 --- /dev/null +++ b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithFilesAndShardingApplicationStartupIT.java @@ -0,0 +1,15 @@ +package de.juplo.kafka.chat.backend; + +import org.springframework.boot.test.context.SpringBootTest; + + +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "chat.backend.inmemory.storage-strategy=files", + "chat.backend.inmemory.sharding-strategy=kafkalike", + "chat.backend.inmemory.num-shards=10", + "chat.backend.inmemory.owned-shards=2" }) +class InMemoryWithFilesAndShardingApplicationStartupIT extends AbstractApplicationStartupIT +{ +} diff --git a/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithFilesApplicationStartupIT.java b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithFilesApplicationStartupIT.java new file mode 100644 index 00000000..8c882922 --- /dev/null +++ b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithFilesApplicationStartupIT.java @@ -0,0 +1,13 @@ +package de.juplo.kafka.chat.backend; + +import org.springframework.boot.test.context.SpringBootTest; + + +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "chat.backend.inmemory.sharding-strategy=none", + "chat.backend.inmemory.storage-strategy=files" }) +class InMemoryWithFilesApplicationStartupIT extends AbstractApplicationStartupIT +{ +} diff --git a/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithMongoDbApplicationStartupIT.java b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithMongoDbApplicationStartupIT.java new file mode 100644 index 00000000..234cb3f3 --- /dev/null +++ b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithMongoDbApplicationStartupIT.java @@ -0,0 +1,44 @@ +package de.juplo.kafka.chat.backend; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + + +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.data.mongodb.host=localhost", + "spring.data.mongodb.database=test", + "chat.backend.inmemory.sharding-strategy=none", + "chat.backend.inmemory.storage-strategy=mongodb" }) +@Testcontainers +@Slf4j +class InMemoryWithMongoDbApplicationStartupIT extends AbstractApplicationStartupIT +{ + private static final int MONGODB_PORT = 27017; + + @Container + private static final GenericContainer CONTAINER = + new GenericContainer("mongo:6").withExposedPorts(MONGODB_PORT); + + @DynamicPropertySource + static void addMongoPortProperty(DynamicPropertyRegistry registry) + { + registry.add("spring.data.mongodb.port", () -> CONTAINER.getMappedPort(MONGODB_PORT)); + } + + @BeforeEach + void setUpLogging() + { + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log); + CONTAINER.followOutput(logConsumer); + } +} diff --git a/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageAndShardingApplicationStartupIT.java b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageAndShardingApplicationStartupIT.java new file mode 100644 index 00000000..9832c28a --- /dev/null +++ b/src/test/java/de/juplo/kafka/chat/backend/InMemoryWithNoStorageAndShardingApplicationStartupIT.java @@ -0,0 +1,15 @@ +package de.juplo.kafka.chat.backend; + +import org.springframework.boot.test.context.SpringBootTest; + + +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "chat.backend.inmemory.storage-strategy=none", + "chat.backend.inmemory.sharding-strategy=kafkalike", + "chat.backend.inmemory.num-shards=10", + "chat.backend.inmemory.owned-shards=2" }) +class InMemoryWithNoStorageAndShardingApplicationStartupIT extends AbstractApplicationStartupIT +{ +} diff --git a/src/test/java/de/juplo/kafka/chat/backend/KafkaApplicationStartupIT.java b/src/test/java/de/juplo/kafka/chat/backend/KafkaApplicationStartupIT.java new file mode 100644 index 00000000..27fcd113 --- /dev/null +++ b/src/test/java/de/juplo/kafka/chat/backend/KafkaApplicationStartupIT.java @@ -0,0 +1,31 @@ +package de.juplo.kafka.chat.backend; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.kafka.test.context.EmbeddedKafka; + +import static de.juplo.kafka.chat.backend.KafkaApplicationStartupIT.DATA_TOPIC; +import static de.juplo.kafka.chat.backend.KafkaApplicationStartupIT.INFO_TOPIC; + + +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = { + "spring.main.allow-bean-definition-overriding=true", + "chat.backend.services=kafka", + "chat.backend.kafka.client-id-PREFIX=TEST", + "chat.backend.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}", + "spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}", + "chat.backend.kafka.info-channel-topic=" + INFO_TOPIC, + "chat.backend.kafka.data-channel-topic=" + DATA_TOPIC, + "chat.backend.kafka.num-partitions=10", + }) +@EmbeddedKafka( + topics = { INFO_TOPIC, DATA_TOPIC }, + partitions = 10) +@Slf4j +class KafkaApplicationStartupIT extends AbstractApplicationStartupIT +{ + final static String INFO_TOPIC = "KAFKA_APPLICATION_STARTUP_IT_INFO_CHANNEL"; + final static String DATA_TOPIC = "KAFKA_APPLICATION_STARTUP_IT_DATA_CHANNEL"; +} diff --git a/src/test/java/de/juplo/kafka/chat/backend/implementation/kafka/InfoChannelTest.java b/src/test/java/de/juplo/kafka/chat/backend/implementation/kafka/InfoChannelTest.java new file mode 100644 index 00000000..c651a5bd --- /dev/null +++ b/src/test/java/de/juplo/kafka/chat/backend/implementation/kafka/InfoChannelTest.java @@ -0,0 +1,109 @@ +package de.juplo.kafka.chat.backend.implementation.kafka; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import de.juplo.kafka.chat.backend.ChatBackendProperties; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.common.TopicPartition; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.test.context.EmbeddedKafka; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import java.time.Clock; +import java.time.Duration; +import java.util.List; + +import static de.juplo.kafka.chat.backend.domain.ChatHomeServiceWithShardsTest.NUM_SHARDS; +import static de.juplo.kafka.chat.backend.implementation.kafka.InfoChannelTest.DATA_TOPIC; +import static de.juplo.kafka.chat.backend.implementation.kafka.InfoChannelTest.INFO_TOPIC; +import static org.assertj.core.api.Assertions.assertThat; + + +@SpringJUnitConfig(classes = { + KafkaAutoConfiguration.class, + TaskExecutionAutoConfiguration.class, + KafkaServicesConfiguration.class, + InfoChannelTest.InfoChannelTestConfiguration.class +}) +@EnableConfigurationProperties(ChatBackendProperties.class) +@TestPropertySource(properties = { + "chat.backend.services=kafka", + "chat.backend.kafka.client-id-PREFIX=TEST", + "chat.backend.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}", + "spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}", + "chat.backend.kafka.info-channel-topic=" + INFO_TOPIC, + "chat.backend.kafka.data-channel-topic=" + DATA_TOPIC, + "chat.backend.kafka.num-partitions=" + NUM_SHARDS, +}) +@EmbeddedKafka( + topics = { INFO_TOPIC, DATA_TOPIC }, + partitions = NUM_SHARDS) +@Slf4j +public class InfoChannelTest +{ + final static String INFO_TOPIC = "INFO_CHANNEL_TEST_INFO"; + final static String DATA_TOPIC = "INFO_CHANNEL_TEST_DATA"; + + + @Autowired + InfoChannel infoChannel; + + + @Test + @DisplayName("The loading completes, if the topic is empty") + void testLoadingCompletesIfTopicEmpty() + { + Awaitility + .await() + .atMost(Duration.ofSeconds(15)) + .untilAsserted(() -> assertThat(infoChannel.getChannelState()).isEqualTo(ChannelState.READY)); + } + + + static class InfoChannelTestConfiguration + { + @Bean + WorkAssignor infoChannelWorkAssignor() + { + return consumer -> + { + List partitions = consumer + .partitionsFor(INFO_TOPIC) + .stream() + .map(partitionInfo -> new TopicPartition(INFO_TOPIC, partitionInfo.partition())) + .toList(); + consumer.assign(partitions); + }; + } + + @Bean + WorkAssignor dataChannelWorkAssignor() + { + return consumer -> log.info("No work is assigned to the DataChannel!"); + } + + @Bean + ObjectMapper objectMapper() + { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + return objectMapper; + } + + @Bean + Clock clock() + { + return Clock.systemDefaultZone(); + } + } +}