diff --git a/.serena/project.yml b/.serena/project.yml index 7037819e..d75fd7d9 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -1,17 +1,10 @@ -# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby) -# * For C, use cpp -# * For JavaScript, use typescript -# Special requirements: -# * csharp: Requires the presence of a .sln file in the project folder. -language: java -# whether to use the project's gitignore file to ignore files -# Added on 2025-04-07 +# whether to use project's .gitignore files to ignore files ignore_all_files_in_gitignore: true -# list of additional paths to ignore -# same syntax as gitignore, so you can use * and ** -# Was previously called `ignored_dirs`, please update your config if you are using that. -# Added (renamed) on 2025-04-07 + +# list of additional paths to ignore in this project. +# Same syntax as gitignore, so you can use * and **. +# Note: global ignored_paths from serena_config.yml are also applied additively. ignored_paths: [] # whether the project is in read-only mode @@ -19,8 +12,9 @@ ignored_paths: [] # Added on 2025-04-18 read_only: false - -# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. +# list of tool names to exclude. +# This extends the existing exclusions (e.g. from the global configuration) +# # Below is the complete list of tools for convenience. # To make sure you have the latest list of tools, and to view their descriptions, # execute `uv run scripts/print_tool_overview.py`. @@ -64,5 +58,95 @@ excluded_tools: [] # initial prompt for the project. It will always be given to the LLM upon activating the project # (contrary to the memories, which are loaded on demand). initial_prompt: "" - +# the name by which the project can be referenced within Serena project_name: "greetings" + +# list of mode names to that are always to be included in the set of active modes +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this setting overrides the global configuration. +# Set this to [] to disable base modes for this project. +# Set this to a list of mode names to always include the respective modes for this project. +base_modes: + +# list of mode names that are to be activated by default. +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this overrides the setting from the global configuration (serena_config.yml). +# This setting can, in turn, be overridden by CLI parameters (--mode). +default_modes: + +# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default). +# This extends the existing inclusions (e.g. from the global configuration). +included_optional_tools: [] + +# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. +# This cannot be combined with non-empty excluded_tools or included_optional_tools. +fixed_tools: [] + +# the encoding used by text files in the project +# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings +encoding: utf-8 + + +# list of languages for which language servers are started; choose from: +# al bash clojure cpp csharp +# csharp_omnisharp dart elixir elm erlang +# fortran fsharp go groovy haskell +# java julia kotlin lua markdown +# matlab nix pascal perl php +# php_phpactor powershell python python_jedi r +# rego ruby ruby_solargraph rust scala +# swift terraform toml typescript typescript_vts +# vue yaml zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# Note: +# - For C, use cpp +# - For JavaScript, use typescript +# - For Free Pascal/Lazarus, use pascal +# Special requirements: +# Some languages require additional setup/installations. +# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers +# When using multiple languages, the first language server that supports a given file will be used for that file. +# The first language is the default language and the respective language server will be used as a fallback. +# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +languages: +- java + +# time budget (seconds) per tool call for the retrieval of additional symbol information +# such as docstrings or parameter information. +# This overrides the corresponding setting in the global configuration; see the documentation there. +# If null or missing, use the setting from the global configuration. +symbol_info_budget: + +# The language backend to use for this project. +# If not set, the global setting from serena_config.yml is used. +# Valid values: LSP, JetBrains +# Note: the backend is fixed at startup. If a project with a different backend +# is activated post-init, an error will be returned. +language_backend: + +# list of regex patterns which, when matched, mark a memory entry as read‑only. +# Extends the list from the global configuration, merging the two lists. +read_only_memory_patterns: [] + +# line ending convention to use when writing source files. +# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) +# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. +line_ending: + +# list of regex patterns for memories to completely ignore. +# Matching memories will not appear in list_memories or activate_project output +# and cannot be accessed via read_memory or write_memory. +# To access ignored memory files, use the read_file tool on the raw file path. +# Extends the list from the global configuration, merging the two lists. +# Example: ["_archive/.*", "_episodes/.*"] +ignored_memory_patterns: [] + +# advanced configuration option allowing to configure language server-specific options. +# Maps the language key to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. +# No documentation on options means no options are available. +ls_specific_settings: {} diff --git a/greetings-parent/pom.xml b/greetings-parent/pom.xml index 043af322..11b34303 100644 --- a/greetings-parent/pom.xml +++ b/greetings-parent/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.5.4 + 4.0.4 net.bakaar @@ -14,15 +14,16 @@ pom 25 - 7.23.0 - 5.13.2 - 1.21.2 - 4.6.17 - 0.8.13 + 7.34.3 + 6.0.3 + 2.0.4 + 4.6.20 + 0.8.14 4.3.0 1.4.8 - 3.13.1 - 0.46.0 + 3.13.2 + 6.0.0 + 0.48.1 ${project.groupId}:${project.artifactId} mckratt @@ -42,6 +43,13 @@ pom import + + org.testcontainers + testcontainers-bom + ${testcontainers.version} + pom + import + io.cucumber cucumber-java @@ -60,24 +68,6 @@ ${cucumber.version} test - - org.testcontainers - junit-jupiter - ${testcontainer.version} - test - - - org.testcontainers - postgresql - ${testcontainer.version} - test - - - org.awaitility - awaitility - ${awaitility.version} - test - au.com.dius.pact.provider junit5spring @@ -101,11 +91,32 @@ ${to-string-verifier.version} test + + io.rest-assured + rest-assured-bom + ${rest-assured.version} + pom + import + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + io.fabric8 docker-maven-plugin @@ -126,7 +137,6 @@ org.apache.maven.plugins maven-surefire-plugin - ${maven-surefire-plugin.version} ${skipUTs} true @@ -136,7 +146,6 @@ org.apache.maven.plugins maven-failsafe-plugin - ${maven-failsafe-plugin.version} true all diff --git a/greetings-service/greetings-bootstrap/pom.xml b/greetings-service/greetings-bootstrap/pom.xml index 0fe82ee1..61bb7e06 100644 --- a/greetings-service/greetings-bootstrap/pom.xml +++ b/greetings-service/greetings-bootstrap/pom.xml @@ -40,8 +40,8 @@ postgresql - org.flywaydb - flyway-core + org.springframework.boot + spring-boot-starter-flyway org.flywaydb @@ -67,7 +67,7 @@ org.testcontainers - postgresql + testcontainers-postgresql test @@ -76,14 +76,8 @@ test - org.springframework.kafka - spring-kafka-test - - - org.slf4j - slf4j-log4j12 - - + org.springframework.boot + spring-boot-starter-kafka-test test diff --git a/greetings-service/greetings-bootstrap/src/main/resources/config/application.properties b/greetings-service/greetings-bootstrap/src/main/resources/config/application.properties index 107d8992..2e413ff3 100644 --- a/greetings-service/greetings-bootstrap/src/main/resources/config/application.properties +++ b/greetings-service/greetings-bootstrap/src/main/resources/config/application.properties @@ -1,6 +1,6 @@ spring.main.lazy-initialization=true -management.endpoints.enabled-by-default=false -management.endpoint.info.enabled=true +management.endpoints.access.default=none +management.endpoint.info.access=read-only management.endpoints.jmx.exposure.exclude=* management.endpoints.web.exposure.include=info management.info.env.enabled=true diff --git a/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/ActuatorInfoIT.java b/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/ActuatorInfoIT.java index 6035bd43..d997720b 100644 --- a/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/ActuatorInfoIT.java +++ b/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/ActuatorInfoIT.java @@ -4,10 +4,11 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; +import org.springframework.boot.kafka.autoconfigure.KafkaAutoConfiguration; +import org.springframework.boot.resttestclient.TestRestTemplate; +import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpStatus; import org.springframework.test.context.bean.override.mockito.MockitoBean; @@ -16,8 +17,8 @@ @SpringBootTest(webEnvironment = RANDOM_PORT) -@AutoConfigureMockMvc -@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) +@AutoConfigureTestRestTemplate +@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, KafkaAutoConfiguration.class}) class ActuatorInfoIT { @MockitoBean private GreetingRepository repository; diff --git a/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/BootstrapCucumberLauncherIT.java b/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/BootstrapCucumberLauncherIT.java index d298ffe9..258f4e05 100644 --- a/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/BootstrapCucumberLauncherIT.java +++ b/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/BootstrapCucumberLauncherIT.java @@ -1,16 +1,17 @@ package net.bakaar.greetings.servicetest; -import org.junit.platform.suite.api.*; +import org.junit.platform.suite.api.ConfigurationParameter; +import org.junit.platform.suite.api.IncludeEngines; +import org.junit.platform.suite.api.SelectClasspathResource; +import org.junit.platform.suite.api.Suite; import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; @Suite @IncludeEngines("cucumber") -@SelectClasspathResources({ - @SelectClasspathResource("GreetingsCreation.feature"), - @SelectClasspathResource("GreetingsUpdate.feature"), - @SelectClasspathResource("GreetingsStats.feature") -}) +@SelectClasspathResource("GreetingsCreation.feature") +@SelectClasspathResource("GreetingsUpdate.feature") +//@SelectClasspathResource("GreetingsStats.feature") @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "net.bakaar.greetings.servicetest.glue") public class BootstrapCucumberLauncherIT { } diff --git a/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/GreetingsPactProviderIT.java b/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/GreetingsPactProviderIT.java index acae2d4d..3355848d 100644 --- a/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/GreetingsPactProviderIT.java +++ b/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/GreetingsPactProviderIT.java @@ -13,8 +13,8 @@ import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; +import org.springframework.boot.kafka.autoconfigure.KafkaAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.bean.override.mockito.MockitoBean; diff --git a/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/glue/CucumberSpringContextConfiguration.java b/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/glue/CucumberSpringContextConfiguration.java index 46d06b9d..6b6443f2 100644 --- a/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/glue/CucumberSpringContextConfiguration.java +++ b/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/glue/CucumberSpringContextConfiguration.java @@ -6,7 +6,7 @@ import org.springframework.kafka.test.context.EmbeddedKafka; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; -import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.postgresql.PostgreSQLContainer; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; diff --git a/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/glue/GreetingsBootstrapCreationSteps.java b/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/glue/GreetingsBootstrapCreationSteps.java index 7dd7a4b3..484b117f 100644 --- a/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/glue/GreetingsBootstrapCreationSteps.java +++ b/greetings-service/greetings-bootstrap/src/test/java/net/bakaar/greetings/servicetest/glue/GreetingsBootstrapCreationSteps.java @@ -1,7 +1,5 @@ package net.bakaar.greetings.servicetest.glue; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; @@ -21,9 +19,11 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.kafka.core.DefaultKafkaConsumerFactory; import org.springframework.kafka.core.KafkaAdmin; -import org.springframework.kafka.support.serializer.JsonDeserializer; +import org.springframework.kafka.support.serializer.JacksonJsonDeserializer; import org.springframework.kafka.test.EmbeddedKafkaBroker; import org.springframework.kafka.test.utils.KafkaTestUtils; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; import java.net.URI; import java.time.Duration; @@ -97,7 +97,7 @@ public void iGetTheMessage(String message) { } @Then("a Greeting is created") - public void a_greeting_is_created() throws JsonProcessingException { + public void a_greeting_is_created() throws JacksonException { Consumer consumer = createConsumer(); ConsumerRecord consumedRecord = KafkaTestUtils.getSingleRecord(consumer, messageProperties.getTopicName(), Duration.ofMillis(10000)); var message = consumedRecord.value(); @@ -111,9 +111,9 @@ public void a_greeting_is_created() throws JsonProcessingException { @NotNull private Consumer createConsumer() { var consumerProps = KafkaTestUtils.consumerProps("testGroup", "true", this.embeddedKafka); - consumerProps.put(VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class); + consumerProps.put(VALUE_DESERIALIZER_CLASS_CONFIG, JacksonJsonDeserializer.class); consumerProps.put(KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); - consumerProps.put(JsonDeserializer.TRUSTED_PACKAGES, "net.bakaar.*"); + consumerProps.put(JacksonJsonDeserializer.TRUSTED_PACKAGES, "net.bakaar.*"); var factory = new DefaultKafkaConsumerFactory(consumerProps); Consumer consumer = factory.createConsumer(); embeddedKafka.consumeFromAnEmbeddedTopic(consumer, TEST_TOPIC); diff --git a/greetings-service/greetings-domain/src/main/java/net/bakaar/greetings/domain/GreetingType.java b/greetings-service/greetings-domain/src/main/java/net/bakaar/greetings/domain/GreetingType.java index 6d63d7bc..d9752d12 100644 --- a/greetings-service/greetings-domain/src/main/java/net/bakaar/greetings/domain/GreetingType.java +++ b/greetings-service/greetings-domain/src/main/java/net/bakaar/greetings/domain/GreetingType.java @@ -4,13 +4,11 @@ import lombok.RequiredArgsConstructor; import net.bakaar.greetings.domain.exception.GreetingWrongTypeException; -import static java.lang.String.format; - @RequiredArgsConstructor public enum GreetingType { - BIRTHDAY(name -> format("Happy Birthday %s !", name)), - ANNIVERSARY(name -> format("Joyful Anniversary %s !", name)), - CHRISTMAS(name -> format("Merry Christmas %s !", name)); + BIRTHDAY(name -> "Happy Birthday %s !".formatted(name)), + ANNIVERSARY(name -> "Joyful Anniversary %s !".formatted(name)), + CHRISTMAS(name -> "Merry Christmas %s !".formatted(name)); private final MessageCreator messageCreator; diff --git a/greetings-service/greetings-persistence/pom.xml b/greetings-service/greetings-persistence/pom.xml index c081894c..ccd0d00f 100644 --- a/greetings-service/greetings-persistence/pom.xml +++ b/greetings-service/greetings-persistence/pom.xml @@ -23,6 +23,11 @@ org.springframework.boot spring-boot-starter-data-jpa + + org.springframework.boot + spring-boot-starter-data-jpa-test + test + com.h2database h2 diff --git a/greetings-service/greetings-persistence/src/test/java/net/bakaar/greetings/persist/GreetingRepositoryJPAAdapterIT.java b/greetings-service/greetings-persistence/src/test/java/net/bakaar/greetings/persist/GreetingRepositoryJPAAdapterIT.java index e6433806..42d397bf 100644 --- a/greetings-service/greetings-persistence/src/test/java/net/bakaar/greetings/persist/GreetingRepositoryJPAAdapterIT.java +++ b/greetings-service/greetings-persistence/src/test/java/net/bakaar/greetings/persist/GreetingRepositoryJPAAdapterIT.java @@ -4,7 +4,7 @@ import net.bakaar.greetings.domain.GreetingType; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest; import org.springframework.context.annotation.Import; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; diff --git a/greetings-service/greetings-producer/pom.xml b/greetings-service/greetings-producer/pom.xml index 10c4ba95..14bb6a32 100644 --- a/greetings-service/greetings-producer/pom.xml +++ b/greetings-service/greetings-producer/pom.xml @@ -19,27 +19,17 @@ greetings-domain ${project.version} - - org.springframework.kafka - spring-kafka - org.springframework.boot - spring-boot-starter + spring-boot-starter-kafka - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 + tools.jackson.core + jackson-databind - org.springframework.kafka - spring-kafka-test - - - org.slf4j - slf4j-log4j12 - - + org.springframework.boot + spring-boot-starter-kafka-test test diff --git a/greetings-service/greetings-producer/src/main/java/net/bakaar/greetings/message/producer/DirectEventEmitterAdapter.java b/greetings-service/greetings-producer/src/main/java/net/bakaar/greetings/message/producer/DirectEventEmitterAdapter.java index 5e02473b..27768e42 100644 --- a/greetings-service/greetings-producer/src/main/java/net/bakaar/greetings/message/producer/DirectEventEmitterAdapter.java +++ b/greetings-service/greetings-producer/src/main/java/net/bakaar/greetings/message/producer/DirectEventEmitterAdapter.java @@ -1,12 +1,12 @@ package net.bakaar.greetings.message.producer; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import net.bakaar.greetings.domain.event.EventEmitter; import net.bakaar.greetings.domain.event.GreetingsEvent; import net.bakaar.greetings.message.GreetingsMessage; import org.springframework.kafka.core.KafkaTemplate; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; import java.net.URI; @@ -24,7 +24,7 @@ public void emit(GreetingsEvent event) { URI.create("https://bakaar.net/greetings/events/greeting-created"), mapper.writeValueAsString(event)) ); - } catch (JsonProcessingException e) { + } catch (JacksonException e) { throw new ProducerException(e); } } diff --git a/greetings-service/greetings-producer/src/main/java/net/bakaar/greetings/message/producer/GreetingsProducerConfiguration.java b/greetings-service/greetings-producer/src/main/java/net/bakaar/greetings/message/producer/GreetingsProducerConfiguration.java index 82e9aca5..95695d39 100644 --- a/greetings-service/greetings-producer/src/main/java/net/bakaar/greetings/message/producer/GreetingsProducerConfiguration.java +++ b/greetings-service/greetings-producer/src/main/java/net/bakaar/greetings/message/producer/GreetingsProducerConfiguration.java @@ -1,18 +1,17 @@ package net.bakaar.greetings.message.producer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import net.bakaar.greetings.domain.event.EventEmitter; import net.bakaar.greetings.message.GreetingsMessage; import org.apache.kafka.common.serialization.StringSerializer; -import org.springframework.boot.autoconfigure.kafka.DefaultKafkaProducerFactoryCustomizer; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.kafka.autoconfigure.DefaultKafkaProducerFactoryCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.annotation.EnableKafka; import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.kafka.support.serializer.JsonSerializer; +import org.springframework.kafka.support.serializer.JacksonJsonSerializer; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.json.JsonMapper; import java.util.Map; @@ -32,16 +31,13 @@ EventEmitter eventEmitter(GreetingsProducerProperties properties, KafkaTemplate< @Bean DefaultKafkaProducerFactoryCustomizer producerFactoryCustomizer() { return producerFactory -> { - Map properties = Map.of(VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class, + Map properties = Map.of(VALUE_SERIALIZER_CLASS_CONFIG, JacksonJsonSerializer.class, KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); producerFactory.updateConfigs(properties); }; } private ObjectMapper createMapper() { - var mapper = new ObjectMapper(); - mapper.registerModule(new JavaTimeModule()); - mapper.disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); - return mapper; + return JsonMapper.builder().build(); } } diff --git a/greetings-service/greetings-producer/src/test/java/net/bakaar/greetings/message/producer/DirectEventEmitterAdapterTest.java b/greetings-service/greetings-producer/src/test/java/net/bakaar/greetings/message/producer/DirectEventEmitterAdapterTest.java index 0d7a4957..ef4a0e38 100644 --- a/greetings-service/greetings-producer/src/test/java/net/bakaar/greetings/message/producer/DirectEventEmitterAdapterTest.java +++ b/greetings-service/greetings-producer/src/test/java/net/bakaar/greetings/message/producer/DirectEventEmitterAdapterTest.java @@ -1,7 +1,5 @@ package net.bakaar.greetings.message.producer; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import net.bakaar.greetings.domain.event.GreetingsEvent; import net.bakaar.greetings.message.GreetingsMessage; import org.junit.jupiter.api.Test; @@ -11,6 +9,8 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.kafka.core.KafkaTemplate; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; @@ -34,7 +34,7 @@ class DirectEventEmitterAdapterTest { private GreetingsEvent event; @Test - void should_transform_payload_to_json_and_call_kafka() throws JsonProcessingException { + void should_transform_payload_to_json_and_call_kafka() throws JacksonException { // Arrange var type = "https://bakaar.net/greetings/events/greeting-created"; var payload = "I'm a payload"; @@ -53,9 +53,9 @@ void should_transform_payload_to_json_and_call_kafka() throws JsonProcessingExce } @Test - void should_throw_a_runtimeException() throws JsonProcessingException { + void should_throw_a_runtimeException() throws JacksonException { // Arrange - var e = mock(JsonProcessingException.class); + var e = mock(JacksonException.class); given(jsonMapper.writeValueAsString(any())).willThrow(e); // Act var thrown = catchThrowable(() -> adapter.emit(event)); diff --git a/greetings-service/greetings-producer/src/test/java/net/bakaar/greetings/message/producer/ProducerPactIT.java b/greetings-service/greetings-producer/src/test/java/net/bakaar/greetings/message/producer/ProducerPactIT.java index 7e402bd0..22a2bccf 100644 --- a/greetings-service/greetings-producer/src/test/java/net/bakaar/greetings/message/producer/ProducerPactIT.java +++ b/greetings-service/greetings-producer/src/test/java/net/bakaar/greetings/message/producer/ProducerPactIT.java @@ -6,8 +6,6 @@ import au.com.dius.pact.provider.junit5.PactVerificationInvocationContextProvider; import au.com.dius.pact.provider.junitsupport.Provider; import au.com.dius.pact.provider.junitsupport.loader.PactFolder; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import net.bakaar.greetings.domain.Greeting; import net.bakaar.greetings.domain.event.EventEmitter; import net.bakaar.greetings.domain.event.GreetingCreated; @@ -21,6 +19,8 @@ import org.springframework.kafka.core.KafkaTemplate; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.ObjectMapper; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -49,7 +49,7 @@ void before(PactVerificationContext context) { } @PactVerifyProvider("A greetings created message") - String send_greeting_created_message() throws JsonProcessingException { + String send_greeting_created_message() throws JacksonException { var event = GreetingCreated.of(Greeting.of("birthday").to("toto").build()); emitter.emit(event); var captor = ArgumentCaptor.forClass(GreetingsMessage.class); diff --git a/greetings-service/greetings-rest/pom.xml b/greetings-service/greetings-rest/pom.xml index 504fc32f..421a2041 100644 --- a/greetings-service/greetings-rest/pom.xml +++ b/greetings-service/greetings-rest/pom.xml @@ -16,7 +16,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc net.bakaar diff --git a/greetings-service/greetings-rest/src/test/java/net/bakaar/greetings/rest/GreetingsControllerIT.java b/greetings-service/greetings-rest/src/test/java/net/bakaar/greetings/rest/GreetingsControllerIT.java index 744a2d17..5942cef0 100644 --- a/greetings-service/greetings-rest/src/test/java/net/bakaar/greetings/rest/GreetingsControllerIT.java +++ b/greetings-service/greetings-rest/src/test/java/net/bakaar/greetings/rest/GreetingsControllerIT.java @@ -5,8 +5,8 @@ import net.bakaar.greetings.domain.UpdateGreetingCommand; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.context.annotation.Import; import org.springframework.http.HttpHeaders; import org.springframework.test.context.bean.override.mockito.MockitoBean; diff --git a/greetings-service/greetings-rest/src/test/java/net/bakaar/greetings/rest/glue/GreetingsCreationSteps.java b/greetings-service/greetings-rest/src/test/java/net/bakaar/greetings/rest/glue/GreetingsCreationSteps.java index 10d202f4..d67f53ae 100644 --- a/greetings-service/greetings-rest/src/test/java/net/bakaar/greetings/rest/glue/GreetingsCreationSteps.java +++ b/greetings-service/greetings-rest/src/test/java/net/bakaar/greetings/rest/glue/GreetingsCreationSteps.java @@ -1,7 +1,5 @@ package net.bakaar.greetings.rest.glue; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; @@ -11,13 +9,15 @@ import net.bakaar.greetings.rest.IdentifiedGreetingMessage; import net.bakaar.greetings.rest.UpdateGreetingCommandDTO; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.restclient.test.autoconfigure.AutoConfigureRestClient; +import org.springframework.boot.resttestclient.TestRestTemplate; +import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.HttpStatus; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; +import tools.jackson.databind.ObjectMapper; import java.net.URI; import java.util.UUID; @@ -28,7 +28,8 @@ @CucumberContextConfiguration @SpringBootTest(webEnvironment = RANDOM_PORT) -@AutoConfigureMockMvc +@AutoConfigureRestClient +@AutoConfigureTestRestTemplate public class GreetingsCreationSteps { @Autowired @@ -66,7 +67,7 @@ public void iCreateAGreetingForName(String type, String name) { } @When("I change the type to {word}") - public void i_change_the_type_to(String type) throws JsonProcessingException { + public void i_change_the_type_to(String type) { var updateGreetingCommand = new UpdateGreetingCommandDTO(); updateGreetingCommand.setNewType(type); var identifier = jsonMapper.readValue(response.getBody(), IdentifiedGreetingMessage.class).id(); @@ -85,7 +86,7 @@ public void iGetTheMessage(String message) { } @Then("a Greeting is created") - public void a_greeting_is_created() throws JsonProcessingException { + public void a_greeting_is_created() { var identifier = jsonMapper.readValue(response.getBody(), IdentifiedGreetingMessage.class).id(); var greeting = repository.find(UUID.fromString(identifier)); assertThat(greeting).isNotEmpty(); diff --git a/greetings-service/pom.xml b/greetings-service/pom.xml index f9374500..6125dabf 100644 --- a/greetings-service/pom.xml +++ b/greetings-service/pom.xml @@ -2,14 +2,6 @@ 4.0.0 - - greetings-bootstrap - greetings-rest - greetings-application - greetings-domain - greetings-persistence - greetings-producer - net.bakaar greetings-parent @@ -21,12 +13,20 @@ greetings-service Demo project for BDD testing and Hexagonal architecture pom + + greetings-bootstrap + greetings-rest + greetings-application + greetings-domain + greetings-persistence + greetings-producer + greetings-service greetings-service greetings-bootstrap/target/site/jacoco-aggregate/jacoco.xml ${basedir}/${aggregate.report.dir} - 3.4.5 + 3.5.1 @@ -65,10 +65,25 @@ test - org.testcontainers - junit-jupiter + org.springframework.boot + spring-boot-test-autoconfigure + test + + + org.springframework.boot + spring-boot-starter-restclient-test + test + + + org.springframework.boot + spring-boot-starter-webmvc-test test + + + + + diff --git a/greetings-stat-service/pom.xml b/greetings-stat-service/pom.xml index e4bb9dcc..7cc9fda0 100644 --- a/greetings-stat-service/pom.xml +++ b/greetings-stat-service/pom.xml @@ -60,11 +60,6 @@ cucumber-junit-platform-engine test - - org.testcontainers - junit-jupiter - test - org.junit.platform junit-platform-suite diff --git a/greetings-stat-service/stat-application/src/test/java/net/bakaar/greetings/stat/application/glue/GreetingsStatsApplicationSteps.java b/greetings-stat-service/stat-application/src/test/java/net/bakaar/greetings/stat/application/glue/GreetingsStatsApplicationSteps.java index e0ab69c0..45270a78 100644 --- a/greetings-stat-service/stat-application/src/test/java/net/bakaar/greetings/stat/application/glue/GreetingsStatsApplicationSteps.java +++ b/greetings-stat-service/stat-application/src/test/java/net/bakaar/greetings/stat/application/glue/GreetingsStatsApplicationSteps.java @@ -29,7 +29,7 @@ public class GreetingsStatsApplicationSteps { private final StatRepository statRepository = mock(StatRepository.class); private final GreetingsRepository greetingsRepository = mock(GreetingsRepository.class); private final StatApplicationService service = new StatApplicationService(statRepository, greetingsRepository); - private GreetingsStats stats = new GreetingsStats(new HashMap<>(Map.of("BIRTHDAY", 0L, "ANNIVERSARY", 0L, "CHRISTMAS", 0L))); + private final GreetingsStats stats = new GreetingsStats(new HashMap<>(Map.of("BIRTHDAY", 0L, "ANNIVERSARY", 0L, "CHRISTMAS", 0L))); private String type = "ANNIVERSARY"; @Given("the christmas greetings counter is equal to {long}") @@ -74,8 +74,8 @@ public void the_greetings_counter_is_equal_to(long value) { @When("I update a greeting") public void i_update_a_greeting() { - // For update scenarios, we don't increment the counter - // This step is essentially a no-op since updates don't affect statistics + // get the stat object from DB + given(statRepository.pop()).willReturn(CompletableFuture.completedFuture(stats)); } @Then("the counter should remain to {long}") diff --git a/greetings-stat-service/stat-bootstrap/pom.xml b/greetings-stat-service/stat-bootstrap/pom.xml index 630c6d69..b21d609c 100644 --- a/greetings-stat-service/stat-bootstrap/pom.xml +++ b/greetings-stat-service/stat-bootstrap/pom.xml @@ -58,32 +58,46 @@ org.flywaydb - flyway-core + flyway-database-postgresql - org.flywaydb - flyway-database-postgresql + org.springframework.boot + spring-boot-starter-flyway org.springframework.boot spring-boot-starter-actuator + + org.springframework.boot + spring-boot-starter-webclient + io.rest-assured rest-assured test - org.springframework.kafka - spring-kafka-test + org.springframework.boot + spring-boot-starter-kafka-test - org.slf4j - slf4j-log4j12 + com.fasterxml.jackson.core + * test + + org.springframework.boot + spring-boot-starter-data-r2dbc-test + test + + + org.springframework.boot + spring-boot-starter-webflux-test + test + org.springframework.boot spring-boot-testcontainers @@ -102,7 +116,12 @@ org.testcontainers - postgresql + testcontainers-postgresql + test + + + org.testcontainers + testcontainers-r2dbc test diff --git a/greetings-stat-service/stat-bootstrap/src/main/java/net/bakaar/greetings/stat/FlywayConfiguration.java b/greetings-stat-service/stat-bootstrap/src/main/java/net/bakaar/greetings/stat/FlywayConfiguration.java index 19724ceb..6c946209 100644 --- a/greetings-stat-service/stat-bootstrap/src/main/java/net/bakaar/greetings/stat/FlywayConfiguration.java +++ b/greetings-stat-service/stat-bootstrap/src/main/java/net/bakaar/greetings/stat/FlywayConfiguration.java @@ -1,8 +1,8 @@ package net.bakaar.greetings.stat; import org.flywaydb.core.Flyway; -import org.springframework.boot.autoconfigure.flyway.FlywayProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.flyway.autoconfigure.FlywayProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/greetings-stat-service/stat-bootstrap/src/main/java/net/bakaar/greetings/stat/StatSpringBootApplication.java b/greetings-stat-service/stat-bootstrap/src/main/java/net/bakaar/greetings/stat/StatSpringBootApplication.java index 338c5a37..af1746db 100644 --- a/greetings-stat-service/stat-bootstrap/src/main/java/net/bakaar/greetings/stat/StatSpringBootApplication.java +++ b/greetings-stat-service/stat-bootstrap/src/main/java/net/bakaar/greetings/stat/StatSpringBootApplication.java @@ -7,7 +7,7 @@ @SpringBootApplication(proxyBeanMethods = false) @EnableTransactionManagement public class StatSpringBootApplication { - public static void main(String[] args) { + static void main() { SpringApplication.run(StatSpringBootApplication.class); } } diff --git a/greetings-stat-service/stat-bootstrap/src/main/resources/config/application.properties b/greetings-stat-service/stat-bootstrap/src/main/resources/config/application.properties index e1e0156b..9de4355b 100644 --- a/greetings-stat-service/stat-bootstrap/src/main/resources/config/application.properties +++ b/greetings-stat-service/stat-bootstrap/src/main/resources/config/application.properties @@ -1,7 +1,7 @@ # FIXME I should have a look to why this break the start of the application. Hint : probably the Datasource Bean not initialized for Flyway. #spring.main.lazy-initialization=true -management.endpoints.enabled-by-default=false -management.endpoint.info.enabled=true +management.endpoints.access.default=none +management.endpoint.info.access=read-only management.endpoints.jmx.exposure.exclude=* management.endpoints.web.exposure.include=info management.info.env.enabled=true diff --git a/greetings-stat-service/stat-bootstrap/src/main/resources/db/migration/V01_00__CreationTable.sql b/greetings-stat-service/stat-bootstrap/src/main/resources/db/migration/V01_00__CreationTable.sql index 02df087c..8e69d2fb 100644 --- a/greetings-stat-service/stat-bootstrap/src/main/resources/db/migration/V01_00__CreationTable.sql +++ b/greetings-stat-service/stat-bootstrap/src/main/resources/db/migration/V01_00__CreationTable.sql @@ -1,6 +1,6 @@ CREATE TABLE t_counter ( pk_t_counter SERIAL PRIMARY KEY NOT NULL, - s_name VARCHAR(255) NOT NULL UNIQUE, + s_name TEXT NOT NULL UNIQUE, l_count INTEGER NOT NULL ); \ No newline at end of file diff --git a/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/ActuatorInfoIT.java b/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/ActuatorInfoIT.java index 5b34dade..781c5d6c 100644 --- a/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/ActuatorInfoIT.java +++ b/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/ActuatorInfoIT.java @@ -5,13 +5,14 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; +import org.springframework.boot.r2dbc.autoconfigure.R2dbcAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.webtestclient.autoconfigure.AutoConfigureWebTestClient; import org.springframework.http.HttpStatus; import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.test.web.reactive.server.assertj.WebTestClientResponse; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -19,7 +20,7 @@ @SpringBootTest(webEnvironment = RANDOM_PORT, properties = {"greetings.message.topic=''"}) -@AutoConfigureMockMvc +@AutoConfigureWebTestClient @EnableAutoConfiguration(exclude = {R2dbcAutoConfiguration.class, DataSourceAutoConfiguration.class}) class ActuatorInfoIT { @MockitoBean @@ -27,16 +28,15 @@ class ActuatorInfoIT { @MockitoBean private Flyway flyway; @Autowired - private TestRestTemplate template; + private WebTestClient template; @Test void should_return_version_number() { // Arrange // Act - var response = template.getForEntity("/actuator/info", String.class); + var response = WebTestClientResponse.from(template.get().uri("/actuator/info") + .exchange().returnResult()); // Assert - assertThat(response).isNotNull(); - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); - assertThat(response.getBody()).contains("\"version\":\"2.0.0\""); + assertThat(response).hasStatus(HttpStatus.OK).bodyText().contains("\"version\":\"2.0.0\""); } } diff --git a/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/glue/BoostrapSpringCucumberContextConfiguration.java b/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/glue/BoostrapSpringCucumberContextConfiguration.java index c246bf4a..384ef384 100644 --- a/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/glue/BoostrapSpringCucumberContextConfiguration.java +++ b/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/glue/BoostrapSpringCucumberContextConfiguration.java @@ -5,10 +5,12 @@ import io.cucumber.spring.CucumberContextConfiguration; import net.bakaar.greetings.stat.StatSpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.testcontainers.context.ImportTestcontainers; +import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.kafka.test.context.EmbeddedKafka; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; -import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.postgresql.PostgreSQLContainer; import static net.bakaar.greetings.stat.bootstrap.glue.GreetingsStatsSteps.topic; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -18,34 +20,30 @@ @SpringBootTest(classes = {StatSpringBootApplication.class}, webEnvironment = RANDOM_PORT, properties = { "spring.profiles.active=test" }) +@ImportTestcontainers public class BoostrapSpringCucumberContextConfiguration { public static final WireMockServer greetings = new WireMockServer(0); + @ServiceConnection private static final PostgreSQLContainer dbContainer = new PostgreSQLContainer("postgres") .withDatabaseName("stats") .withUsername("foo") .withPassword("secret"); - static { dbContainer.start(); greetings.start(); } @AfterAll - static void afterAll() { + public static void afterAll() { dbContainer.stop(); greetings.stop(); } @DynamicPropertySource static void registerProperties(DynamicPropertyRegistry registry) { - registry.add("spring.r2dbc.url", - () -> "r2dbc:postgresql://localhost:%d/%s".formatted( - dbContainer.getFirstMappedPort(), dbContainer.getDatabaseName())); - registry.add("spring.r2dbc.password", dbContainer::getPassword); - registry.add("spring.r2dbc.username", dbContainer::getUsername); registry.add("spring.flyway.url", dbContainer::getJdbcUrl); registry.add("spring.flyway.user", dbContainer::getUsername); registry.add("spring.flyway.password", dbContainer::getPassword); diff --git a/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/glue/GreetingsStatsSteps.java b/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/glue/GreetingsStatsSteps.java index e8c7cd83..9589b1fd 100644 --- a/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/glue/GreetingsStatsSteps.java +++ b/greetings-stat-service/stat-bootstrap/src/test/java/net/bakaar/greetings/stat/bootstrap/glue/GreetingsStatsSteps.java @@ -1,5 +1,6 @@ package net.bakaar.greetings.stat.bootstrap.glue; +import io.cucumber.java.After; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; @@ -19,8 +20,8 @@ import org.springframework.data.relational.core.query.Query; import org.springframework.kafka.core.DefaultKafkaConsumerFactory; import org.springframework.kafka.core.DefaultKafkaProducerFactory; -import org.springframework.kafka.support.serializer.JsonDeserializer; -import org.springframework.kafka.support.serializer.JsonSerializer; +import org.springframework.kafka.support.serializer.JacksonJsonDeserializer; +import org.springframework.kafka.support.serializer.JacksonJsonSerializer; import org.springframework.kafka.test.EmbeddedKafkaBroker; import org.springframework.kafka.test.utils.KafkaTestUtils; @@ -65,6 +66,11 @@ public void the_christmas_greetings_counter_is_equal_to(int counter) { assertThat(ety.getCount()).isEqualTo(counter); } + @After + public void afterEach() { + template.delete(Counter.class).all().block(); + } + @When("I create a greeting") public void i_create_a_greetings() { i_create_a_greetings(""); @@ -79,16 +85,17 @@ public void i_create_a_greetings(String inputType) { var producerFactory = new DefaultKafkaProducerFactory( KafkaTestUtils.producerProps(embeddedKafka)); producerFactory.setKeySerializer(new StringSerializer()); - producerFactory.setValueSerializer(new JsonSerializer<>()); - var producer = producerFactory.createProducer(); - var message = new GreetingsMessage(URI.create("https://bakaar.net/greetings/events/greeting-created"), """ - { - "identifier": "%s", - "raisedAt" : "2010-01-01T12:00:00+01:00" - } - """.formatted(identifier)); - producer.send(new ProducerRecord<>(topic, identifier.toString(), message)); - producer.flush(); + producerFactory.setValueSerializer(new JacksonJsonSerializer<>()); + try (var producer = producerFactory.createProducer()) { + var message = new GreetingsMessage(URI.create("https://bakaar.net/greetings/events/greeting-created"), """ + { + "identifier": "%s", + "raisedAt" : "2010-01-01T12:00:00+01:00" + } + """.formatted(identifier)); + producer.send(new ProducerRecord<>(topic, identifier.toString(), message)); + producer.flush(); + } // Stub the answer from greetings service greetings.stubFor(get(urlEqualTo("/rest/api/v1/greetings/%s".formatted(identifier))).willReturn(aResponse() .withStatus(200) @@ -111,10 +118,10 @@ public void i_create_a_greetings(String inputType) { } private Consumer createConsumer() { - var consumerProps = KafkaTestUtils.consumerProps("testGroup", "true", this.embeddedKafka); - consumerProps.put(VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class); + var consumerProps = KafkaTestUtils.consumerProps(this.embeddedKafka, "testGroup", true); + consumerProps.put(VALUE_DESERIALIZER_CLASS_CONFIG, JacksonJsonDeserializer.class); consumerProps.put(KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); - consumerProps.put(JsonDeserializer.TRUSTED_PACKAGES, "net.bakaar.*"); + consumerProps.put(JacksonJsonDeserializer.TRUSTED_PACKAGES, "net.bakaar.*"); var factory = new DefaultKafkaConsumerFactory(consumerProps); Consumer createdConsumer = factory.createConsumer(); embeddedKafka.consumeFromAnEmbeddedTopic(createdConsumer, topic); @@ -125,7 +132,7 @@ private Consumer createConsumer() { public void the_counter_should_be(Integer counter) { await().until(() -> { var counterDb = template.selectOne(Query.query(CriteriaDefinition.from(Criteria.where("S_NAME").is(type.toUpperCase()))), Counter.class).block(); - log.debug(type + " counter = " + (counterDb != null ? counterDb.getCount() : "unknown")); + log.debug("{} counter = {}", type, counterDb != null ? counterDb.getCount() : "unknown"); return counterDb != null && counterDb.getCount() > 0; }); given().get("http://localhost:%d/rest/api/v1/stats".formatted(port)) diff --git a/greetings-stat-service/stat-consumer/pom.xml b/greetings-stat-service/stat-consumer/pom.xml index c378e7ad..8c418787 100644 --- a/greetings-stat-service/stat-consumer/pom.xml +++ b/greetings-stat-service/stat-consumer/pom.xml @@ -18,17 +18,13 @@ stat-application ${project.version} - - org.springframework.kafka - spring-kafka - org.springframework.boot - spring-boot-starter + spring-boot-starter-kafka - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 + tools.jackson.core + jackson-databind io.cucumber @@ -41,14 +37,8 @@ test - org.springframework.kafka - spring-kafka-test - - - org.slf4j - slf4j-log4j12 - - + org.springframework.boot + spring-boot-starter-kafka-test test diff --git a/greetings-stat-service/stat-consumer/src/main/java/net/bakaar/greetings/stat/message/StatMessageConfiguration.java b/greetings-stat-service/stat-consumer/src/main/java/net/bakaar/greetings/stat/message/StatMessageConfiguration.java index 2219fda6..995f1341 100644 --- a/greetings-stat-service/stat-consumer/src/main/java/net/bakaar/greetings/stat/message/StatMessageConfiguration.java +++ b/greetings-stat-service/stat-consumer/src/main/java/net/bakaar/greetings/stat/message/StatMessageConfiguration.java @@ -1,8 +1,7 @@ package net.bakaar.greetings.stat.message; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import tools.jackson.databind.DeserializationFeature; +import tools.jackson.databind.ObjectMapper; import net.bakaar.greetings.stat.application.StatApplicationService; import net.bakaar.greetings.stat.message.handler.CreatedGreetingEventPayloadHandler; import net.bakaar.greetings.stat.message.handler.GreetingMessagePayloadHandler; @@ -13,6 +12,7 @@ import org.springframework.kafka.annotation.EnableKafka; import org.springframework.kafka.listener.CommonContainerStoppingErrorHandler; import org.springframework.kafka.listener.CommonErrorHandler; +import tools.jackson.databind.json.JsonMapper; @EnableKafka @Configuration(proxyBeanMethods = false) @@ -31,15 +31,11 @@ GreetingMessagePayloadHandler greetingCreatedPayloadHandler(StatApplicationServi } @Bean - GreetingsMessageProcessor greetingsMessageProcessor(){ + GreetingsMessageProcessor greetingsMessageProcessor() { return new GreetingsMessageProcessor(); } private ObjectMapper createJsonMapper() { - var mapper = new ObjectMapper(); - mapper.registerModule(new JavaTimeModule()); - mapper.disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS); - mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - return mapper; + return JsonMapper.builder().build(); } } diff --git a/greetings-stat-service/stat-consumer/src/main/java/net/bakaar/greetings/stat/message/handler/CreatedGreetingEventPayloadHandler.java b/greetings-stat-service/stat-consumer/src/main/java/net/bakaar/greetings/stat/message/handler/CreatedGreetingEventPayloadHandler.java index cd2264a1..0b152412 100644 --- a/greetings-stat-service/stat-consumer/src/main/java/net/bakaar/greetings/stat/message/handler/CreatedGreetingEventPayloadHandler.java +++ b/greetings-stat-service/stat-consumer/src/main/java/net/bakaar/greetings/stat/message/handler/CreatedGreetingEventPayloadHandler.java @@ -1,6 +1,6 @@ package net.bakaar.greetings.stat.message.handler; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import net.bakaar.greetings.stat.application.StatApplicationService; import net.bakaar.greetings.stat.domain.GreetingCreated; diff --git a/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/StatConsumerMessagePactIT.java b/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/StatConsumerMessagePactIT.java index 26642e57..2ad5a0d5 100644 --- a/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/StatConsumerMessagePactIT.java +++ b/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/StatConsumerMessagePactIT.java @@ -9,7 +9,7 @@ import au.com.dius.pact.core.model.annotations.Pact; import au.com.dius.pact.core.model.messaging.Message; import au.com.dius.pact.core.model.messaging.MessagePact; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectMapper; import net.bakaar.greetings.message.GreetingsMessage; import net.bakaar.greetings.stat.application.StatApplicationService; import net.bakaar.greetings.stat.domain.GreetingCreated; diff --git a/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/glue/GreetingsStatsConsumerSteps.java b/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/glue/GreetingsStatsConsumerSteps.java index ce541ceb..2488862f 100644 --- a/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/glue/GreetingsStatsConsumerSteps.java +++ b/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/glue/GreetingsStatsConsumerSteps.java @@ -12,7 +12,7 @@ import org.apache.kafka.common.serialization.StringSerializer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.kafka.core.DefaultKafkaProducerFactory; -import org.springframework.kafka.support.serializer.JsonSerializer; +import org.springframework.kafka.support.serializer.JacksonJsonSerializer; import org.springframework.kafka.test.EmbeddedKafkaBroker; import org.springframework.kafka.test.utils.KafkaTestUtils; import reactor.core.publisher.Mono; @@ -60,7 +60,7 @@ public void i_create_a_greeting(String inputType) { var producerFactory = new DefaultKafkaProducerFactory( KafkaTestUtils.producerProps(embeddedKafka)); producerFactory.setKeySerializer(new StringSerializer()); - producerFactory.setValueSerializer(new JsonSerializer<>()); + producerFactory.setValueSerializer(new JacksonJsonSerializer<>()); var producer = producerFactory.createProducer(); var message = new GreetingsMessage(URI.create("https://bakaar.net/greetings/events/greeting-created"), """ { @@ -94,6 +94,7 @@ public void the_greetings_counter_is_equal_to(long value) { @When("I update a greeting") public void i_update_a_greeting() { + given(statRepository.pop()).willReturn(CompletableFuture.completedFuture(mockedStats)); // Update scenarios don't send new events, so this is essentially a no-op // The counter should remain unchanged } @@ -114,7 +115,7 @@ public void i_create_a_greeting_for_name(String inputName) { var producerFactory = new DefaultKafkaProducerFactory( KafkaTestUtils.producerProps(embeddedKafka)); producerFactory.setKeySerializer(new StringSerializer()); - producerFactory.setValueSerializer(new JsonSerializer<>()); + producerFactory.setValueSerializer(new JacksonJsonSerializer<>()); var producer = producerFactory.createProducer(); var message = new GreetingsMessage(URI.create("https://bakaar.net/greetings/events/greeting-created"), """ { diff --git a/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/handler/CreatedGreetingEventPayloadHandlerTest.java b/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/handler/CreatedGreetingEventPayloadHandlerTest.java index 0bc2167b..643e679b 100644 --- a/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/handler/CreatedGreetingEventPayloadHandlerTest.java +++ b/greetings-stat-service/stat-consumer/src/test/java/net/bakaar/greetings/stat/message/handler/CreatedGreetingEventPayloadHandlerTest.java @@ -1,8 +1,5 @@ package net.bakaar.greetings.stat.message.handler; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.io.JsonEOFException; -import com.fasterxml.jackson.databind.ObjectMapper; import net.bakaar.greetings.stat.application.StatApplicationService; import net.bakaar.greetings.stat.domain.GreetingCreated; import net.bakaar.greetings.stat.message.exception.JsonDeserializationException; @@ -15,6 +12,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; import java.net.URI; @@ -32,7 +31,7 @@ class CreatedGreetingEventPayloadHandlerTest { private StatApplicationService service; @Mock - private ObjectMapper jsonMapper; + private JsonMapper jsonMapper; @InjectMocks private CreatedGreetingEventPayloadHandler handler; @@ -58,7 +57,7 @@ void canHandle_should_return_true() { } @Test - void handle_should_call_jsonMapper_and_service() throws JsonProcessingException { + void handle_should_call_jsonMapper_and_service() { // Arrange var payload = "Payload"; var event = mock(GreetingCreated.class); @@ -74,9 +73,9 @@ void handle_should_call_jsonMapper_and_service() throws JsonProcessingException } @Test - void handle_should_throw_exception() throws JsonProcessingException { + void handle_should_throw_exception() { // Arrange - var cause = new JsonEOFException(null, null, null); + var cause = mock(JacksonException.class); given(jsonMapper.readValue(anyString(), any(Class.class))).willThrow(cause); // Act StepVerifier.create(handler.handle("Whatever")) diff --git a/greetings-stat-service/stat-persistence/pom.xml b/greetings-stat-service/stat-persistence/pom.xml index 27626f76..94a4a2dd 100644 --- a/greetings-stat-service/stat-persistence/pom.xml +++ b/greetings-stat-service/stat-persistence/pom.xml @@ -22,6 +22,11 @@ org.springframework.boot spring-boot-starter-data-r2dbc + + org.springframework.boot + spring-boot-starter-data-r2dbc-test + test + io.r2dbc r2dbc-h2 diff --git a/greetings-stat-service/stat-persistence/src/main/java/net/bakaar/greetings/stat/persistence/Counter.java b/greetings-stat-service/stat-persistence/src/main/java/net/bakaar/greetings/stat/persistence/Counter.java index 9c66bad4..839f99a8 100644 --- a/greetings-stat-service/stat-persistence/src/main/java/net/bakaar/greetings/stat/persistence/Counter.java +++ b/greetings-stat-service/stat-persistence/src/main/java/net/bakaar/greetings/stat/persistence/Counter.java @@ -10,13 +10,13 @@ @Setter @Accessors(chain = true) @Getter -@Table("T_COUNTER") +@Table("t_counter") public class Counter { @Id - @Column("PK_T_COUNTER") + @Column("pk_t_counter") private long id; - @Column("S_NAME") + @Column("s_name") private String name; - @Column("L_COUNT") + @Column("l_count") private long count = 0; } diff --git a/greetings-stat-service/stat-persistence/src/main/resources/persistence.properties b/greetings-stat-service/stat-persistence/src/main/resources/persistence.properties index dc51c0c2..67c42252 100644 --- a/greetings-stat-service/stat-persistence/src/main/resources/persistence.properties +++ b/greetings-stat-service/stat-persistence/src/main/resources/persistence.properties @@ -1,5 +1 @@ -spring.data.r2dbc.repositories.enabled=true -spring.data.jpa.repositories.enabled=false -spring.data.jdbc.repositories.enabled=false -spring.data.ldap.repositories.enabled=false -spring.data.elasticsearch.repositories.enabled=false \ No newline at end of file +spring.data.r2dbc.repositories.enabled=true \ No newline at end of file diff --git a/greetings-stat-service/stat-persistence/src/test/java/net/bakaar/greetings/stat/persistence/StatPersistenceTestApplication.java b/greetings-stat-service/stat-persistence/src/test/java/net/bakaar/greetings/stat/persistence/StatPersistenceTestApplication.java index 36fbbc93..e94a33d9 100644 --- a/greetings-stat-service/stat-persistence/src/test/java/net/bakaar/greetings/stat/persistence/StatPersistenceTestApplication.java +++ b/greetings-stat-service/stat-persistence/src/test/java/net/bakaar/greetings/stat/persistence/StatPersistenceTestApplication.java @@ -20,10 +20,10 @@ public class StatPersistenceTestApplication { void template() { var template = new R2dbcEntityTemplate(connectionFactory); template.getDatabaseClient().sql(""" - CREATE TABLE T_COUNTER - (PK_T_COUNTER SERIAL PRIMARY KEY, - S_NAME VARCHAR(255) NOT NULL UNIQUE, - L_COUNT NUMBER)""") + CREATE TABLE t_counter + (pk_t_counter SERIAL PRIMARY KEY, + s_name TEXT NOT NULL UNIQUE, + l_count INTEGER)""") .fetch() .rowsUpdated() .as(StepVerifier::create) diff --git a/greetings-stat-service/stat-persistence/src/test/java/net/bakaar/greetings/stat/persistence/StatRepositoryAdapterIT.java b/greetings-stat-service/stat-persistence/src/test/java/net/bakaar/greetings/stat/persistence/StatRepositoryAdapterIT.java index 94233b49..a8f3769e 100644 --- a/greetings-stat-service/stat-persistence/src/test/java/net/bakaar/greetings/stat/persistence/StatRepositoryAdapterIT.java +++ b/greetings-stat-service/stat-persistence/src/test/java/net/bakaar/greetings/stat/persistence/StatRepositoryAdapterIT.java @@ -5,7 +5,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.data.r2dbc.DataR2dbcTest; +import org.springframework.boot.data.r2dbc.test.autoconfigure.DataR2dbcTest; import org.springframework.context.annotation.Import; import org.springframework.data.r2dbc.core.R2dbcEntityTemplate; import org.springframework.test.context.ContextConfiguration; @@ -19,7 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat; @DataR2dbcTest(properties = { - "spring.r2dbc.url=r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" + "spring.r2dbc.url=r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;" }) @Import({StatRepositoryAdapter.class}) @ContextConfiguration(classes = StatPersistenceTestApplication.class) diff --git a/greetings-stat-service/stat-rest/pom.xml b/greetings-stat-service/stat-rest/pom.xml index 80568b4a..25d0bb6f 100644 --- a/greetings-stat-service/stat-rest/pom.xml +++ b/greetings-stat-service/stat-rest/pom.xml @@ -22,6 +22,11 @@ org.springframework.boot spring-boot-starter-webflux + + org.springframework.boot + spring-boot-starter-webflux-test + test + io.cucumber cucumber-spring diff --git a/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/StatRestControllerIT.java b/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/StatRestControllerIT.java index 442d6196..c99bec52 100644 --- a/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/StatRestControllerIT.java +++ b/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/StatRestControllerIT.java @@ -4,8 +4,8 @@ import net.bakaar.greetings.stat.domain.GreetingsStats; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; -import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.boot.webflux.test.autoconfigure.WebFluxTest; +import org.springframework.boot.webtestclient.autoconfigure.AutoConfigureWebTestClient; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.reactive.server.WebTestClient; import reactor.core.publisher.Mono; @@ -17,7 +17,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON; @WebFluxTest(controllers = StatRestController.class) -@AutoConfigureWebClient +@AutoConfigureWebTestClient class StatRestControllerIT { @Autowired diff --git a/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/glue/StatRestSpringCucumberContextConfiguration.java b/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/glue/StatRestSpringCucumberContextConfiguration.java index 0db106b2..11a9f4f7 100644 --- a/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/glue/StatRestSpringCucumberContextConfiguration.java +++ b/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/glue/StatRestSpringCucumberContextConfiguration.java @@ -4,15 +4,15 @@ import io.cucumber.spring.CucumberContextConfiguration; import net.bakaar.greetings.stat.application.GreetingsRepository; import net.bakaar.greetings.stat.domain.StatRepository; -import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.webtestclient.autoconfigure.AutoConfigureWebTestClient; import org.springframework.test.context.bean.override.mockito.MockitoBean; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @CucumberContextConfiguration @SpringBootTest(webEnvironment = RANDOM_PORT) -@AutoConfigureWebClient +@AutoConfigureWebTestClient public class StatRestSpringCucumberContextConfiguration { @MockitoBean private GreetingsRepository repository; diff --git a/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/glue/StatsRestSteps.java b/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/glue/StatsRestSteps.java index 26159bd3..c24179b8 100644 --- a/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/glue/StatsRestSteps.java +++ b/greetings-stat-service/stat-rest/src/test/java/net/bakaar/greetings/stat/rest/glue/StatsRestSteps.java @@ -109,6 +109,7 @@ public void i_create_a_greeting_for_name(String name) { var greeting = new Greeting(type, name); given(repository.getGreetingForIdentifier(identifier)).willReturn(Mono.just(greeting)); var event = new GreetingCreated(identifier); + given(statRepository.pop()).willReturn(CompletableFuture.completedFuture(new GreetingsStats(new HashMap<>()))); StepVerifier.create(service.handle(event)) .verifyComplete(); }