diff --git a/build.gradle.kts b/build.gradle.kts index 6f928e21..6b7de427 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ description = "Experiments with Java" allprojects { group = "io.github.mfvanek" - version = "0.2.0" + version = "0.2.1" repositories { mavenLocal() @@ -19,7 +19,7 @@ allprojects { tasks { wrapper { - gradleVersion = "8.7" + gradleVersion = "8.10.2" } } diff --git a/buildSrc/src/main/kotlin/sb-ot-demo.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/sb-ot-demo.java-conventions.gradle.kts index a5437740..c8c0a80b 100644 --- a/buildSrc/src/main/kotlin/sb-ot-demo.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/sb-ot-demo.java-conventions.gradle.kts @@ -20,8 +20,8 @@ dependencies { testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") - errorprone("com.google.errorprone:error_prone_core:2.26.1") - errorprone("jp.skypencil.errorprone.slf4j:errorprone-slf4j:0.1.23") + errorprone("com.google.errorprone:error_prone_core:2.33.0") + errorprone("jp.skypencil.errorprone.slf4j:errorprone-slf4j:0.1.28") } java { diff --git a/common-internal-bom/build.gradle.kts b/common-internal-bom/build.gradle.kts index 9a7b05f8..295431c9 100644 --- a/common-internal-bom/build.gradle.kts +++ b/common-internal-bom/build.gradle.kts @@ -12,7 +12,7 @@ dependencies { api(platform("org.assertj:assertj-bom:3.26.3")) api(platform("org.testcontainers:testcontainers-bom:1.20.2")) api(platform("org.junit:junit-bom:5.11.2")) - api(platform("io.github.mfvanek:pg-index-health-bom:0.13.0")) + api(platform("io.github.mfvanek:pg-index-health-bom:0.13.1")) constraints { api("org.liquibase:liquibase-core:4.29.2") diff --git a/docker/docker-compose-base.yml b/docker/docker-compose-base.yml index 4a777c13..dc3aba7b 100644 --- a/docker/docker-compose-base.yml +++ b/docker/docker-compose-base.yml @@ -22,7 +22,7 @@ services: - jaeger-example zookeeper1: - image: confluentinc/cp-zookeeper:7.5.3 + image: confluentinc/cp-zookeeper:7.7.1 hostname: zookeeper1 container_name: zookeeper1 ports: @@ -35,7 +35,7 @@ services: - jaeger-example kafka1: - image: confluentinc/cp-kafka:7.5.3 + image: confluentinc/cp-kafka:7.7.1 hostname: kafka1 container_name: kafka1 ports: @@ -86,7 +86,7 @@ services: postgres: container_name: postgres - image: postgres:16.2 + image: postgres:16.4 shm_size: "2gb" environment: POSTGRES_DB: "otel_demo_db" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136..a4b76b95 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23a..df97d72b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a42..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 25da30db..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## diff --git a/spring-boot-2-demo-app/build.gradle.kts b/spring-boot-2-demo-app/build.gradle.kts index c5d1418d..a8914d89 100644 --- a/spring-boot-2-demo-app/build.gradle.kts +++ b/spring-boot-2-demo-app/build.gradle.kts @@ -7,7 +7,9 @@ plugins { dependencies { implementation(platform(project(":common-internal-bom"))) - implementation(platform("org.springdoc:springdoc-openapi:1.7.0")) + implementation(platform("org.springdoc:springdoc-openapi:1.7.0")) { + because("version 1.8.0 brings incompatible logging library") + } implementation(platform("org.springframework.boot:spring-boot-dependencies:2.7.18")) implementation(platform("org.springframework.cloud:spring-cloud-dependencies:2021.0.9")) implementation(platform("org.springframework.cloud:spring-cloud-sleuth-otel-dependencies:1.1.4")) @@ -40,6 +42,7 @@ dependencies { testImplementation("org.testcontainers:kafka") testImplementation("org.springframework.kafka:spring-kafka-test") testImplementation("org.awaitility:awaitility") + testImplementation("io.github.mfvanek:pg-index-health-test-starter") } springBoot { diff --git a/spring-boot-2-demo-app/src/main/resources/application.yml b/spring-boot-2-demo-app/src/main/resources/application.yml index 2e676219..4f2fe175 100644 --- a/spring-boot-2-demo-app/src/main/resources/application.yml +++ b/spring-boot-2-demo-app/src/main/resources/application.yml @@ -63,8 +63,9 @@ spring: enable-logging: true log-level: INFO includes: QUERY - template: - query-timeout: 1 # 1 second + jdbc: + template: + query-timeout: 1s management: server: diff --git a/spring-boot-2-demo-app/src/test/java/io/github/mfvanek/spring/boot2/test/IndexesMaintenanceTest.java b/spring-boot-2-demo-app/src/test/java/io/github/mfvanek/spring/boot2/test/IndexesMaintenanceTest.java new file mode 100644 index 00000000..4967c663 --- /dev/null +++ b/spring-boot-2-demo-app/src/test/java/io/github/mfvanek/spring/boot2/test/IndexesMaintenanceTest.java @@ -0,0 +1,57 @@ +package io.github.mfvanek.spring.boot2.test; + +import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnHost; +import io.github.mfvanek.pg.common.maintenance.Diagnostic; +import io.github.mfvanek.pg.model.DbObject; +import io.github.mfvanek.pg.model.column.Column; +import io.github.mfvanek.pg.model.table.Table; +import io.github.mfvanek.spring.boot2.test.support.TestBase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.list; + +class IndexesMaintenanceTest extends TestBase { + + @Autowired + private List> checks; + + @Test + @DisplayName("Always check PostgreSQL version in your tests") + void checkPostgresVersion() { + final String pgVersion = jdbcTemplate.queryForObject("select version();", String.class); + assertThat(pgVersion) + .startsWith("PostgreSQL 16.4"); + } + + @Test + void databaseStructureCheckForPublicSchema() { + assertThat(checks) + .hasSameSizeAs(Diagnostic.values()); + + checks.forEach(check -> { + switch (check.getDiagnostic()) { + case TABLES_WITHOUT_PRIMARY_KEY, TABLES_WITHOUT_DESCRIPTION -> assertThat(check.check()) + .asInstanceOf(list(Table.class)) + .hasSize(1) + .containsExactly(Table.of("databasechangelog", 0L)); + + case COLUMNS_WITHOUT_DESCRIPTION -> assertThat(check.check()) + .asInstanceOf(list(Column.class)) + .hasSize(14) + .allSatisfy(column -> assertThat(column.getTableName()).isEqualTo("databasechangelog")); + + case TABLES_WITH_MISSING_INDEXES -> assertThat(check.check()) + .hasSizeLessThanOrEqualTo(1); // TODO skip runtime checks after https://github.com/mfvanek/pg-index-health/issues/456 + + default -> assertThat(check.check()) + .as(check.getDiagnostic().name()) + .isEmpty(); + } + }); + } +} diff --git a/spring-boot-2-demo-app/src/test/java/io/github/mfvanek/spring/boot2/test/support/KafkaInitializer.java b/spring-boot-2-demo-app/src/test/java/io/github/mfvanek/spring/boot2/test/support/KafkaInitializer.java index 3c415030..ae162a2e 100644 --- a/spring-boot-2-demo-app/src/test/java/io/github/mfvanek/spring/boot2/test/support/KafkaInitializer.java +++ b/spring-boot-2-demo-app/src/test/java/io/github/mfvanek/spring/boot2/test/support/KafkaInitializer.java @@ -16,7 +16,7 @@ public class KafkaInitializer implements ApplicationContextInitializer { - private static final DockerImageName IMAGE = DockerImageName.parse("postgres:16.2"); + private static final DockerImageName IMAGE = DockerImageName.parse("postgres:16.4"); private static final Network NETWORK = Network.newNetwork(); private static final PostgreSQLContainer CONTAINER = new PostgreSQLContainer<>(IMAGE); diff --git a/spring-boot-2-demo-app/src/test/resources/application-test.yml b/spring-boot-2-demo-app/src/test/resources/application-test.yml index c088383e..e69de29b 100644 --- a/spring-boot-2-demo-app/src/test/resources/application-test.yml +++ b/spring-boot-2-demo-app/src/test/resources/application-test.yml @@ -1,4 +0,0 @@ -spring: - jdbc: - template: - query-timeout: 1 # 1 second diff --git a/spring-boot-3-demo-app/build.gradle.kts b/spring-boot-3-demo-app/build.gradle.kts index aafec910..dc4ddb9e 100644 --- a/spring-boot-3-demo-app/build.gradle.kts +++ b/spring-boot-3-demo-app/build.gradle.kts @@ -1,14 +1,14 @@ plugins { id("sb-ot-demo.java-conventions") id("sb-ot-demo.docker") - id("org.springframework.boot") version "3.3.2" + id("org.springframework.boot") version "3.3.4" id("io.freefair.lombok") } dependencies { implementation(platform(project(":common-internal-bom"))) implementation(platform("org.springdoc:springdoc-openapi:2.6.0")) - implementation(platform("org.springframework.boot:spring-boot-dependencies:3.3.2")) + implementation(platform("org.springframework.boot:spring-boot-dependencies:3.3.4")) implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-actuator") @@ -34,6 +34,7 @@ dependencies { testImplementation("org.testcontainers:kafka") testImplementation("org.springframework.kafka:spring-kafka-test") testImplementation("org.awaitility:awaitility") + testImplementation("io.github.mfvanek:pg-index-health-test-starter") } springBoot { diff --git a/spring-boot-3-demo-app/src/main/resources/application.yml b/spring-boot-3-demo-app/src/main/resources/application.yml index f7de3c9b..43ba1c2c 100644 --- a/spring-boot-3-demo-app/src/main/resources/application.yml +++ b/spring-boot-3-demo-app/src/main/resources/application.yml @@ -44,6 +44,9 @@ spring: sasl: mechanism: PLAIN jaas.config: org.apache.kafka.common.security.plain.PlainLoginModule required username="${demo.kafka.opentelemetry.username}" password="${demo.kafka.opentelemetry.password}"; + jdbc: + template: + query-timeout: 1s management: server: @@ -118,8 +121,6 @@ jdbc: enable-logging: true log-level: INFO includes: QUERY - template: - query-timeout: 1 # 1 second --- diff --git a/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/IndexesMaintenanceTest.java b/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/IndexesMaintenanceTest.java new file mode 100644 index 00000000..d19a43f0 --- /dev/null +++ b/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/IndexesMaintenanceTest.java @@ -0,0 +1,57 @@ +package io.github.mfvanek.spring.boot3.test; + +import io.github.mfvanek.pg.common.maintenance.DatabaseCheckOnHost; +import io.github.mfvanek.pg.common.maintenance.Diagnostic; +import io.github.mfvanek.pg.model.DbObject; +import io.github.mfvanek.pg.model.column.Column; +import io.github.mfvanek.pg.model.table.Table; +import io.github.mfvanek.spring.boot3.test.support.TestBase; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.list; + +class IndexesMaintenanceTest extends TestBase { + + @Autowired + private List> checks; + + @Test + @DisplayName("Always check PostgreSQL version in your tests") + void checkPostgresVersion() { + final String pgVersion = jdbcTemplate.queryForObject("select version();", String.class); + assertThat(pgVersion) + .startsWith("PostgreSQL 16.4"); + } + + @Test + void databaseStructureCheckForPublicSchema() { + assertThat(checks) + .hasSameSizeAs(Diagnostic.values()); + + checks.forEach(check -> { + switch (check.getDiagnostic()) { + case TABLES_WITHOUT_PRIMARY_KEY, TABLES_WITHOUT_DESCRIPTION -> assertThat(check.check()) + .asInstanceOf(list(Table.class)) + .hasSize(1) + .containsExactly(Table.of("databasechangelog", 0L)); + + case COLUMNS_WITHOUT_DESCRIPTION -> assertThat(check.check()) + .asInstanceOf(list(Column.class)) + .hasSize(14) + .allSatisfy(column -> assertThat(column.getTableName()).isEqualTo("databasechangelog")); + + case TABLES_WITH_MISSING_INDEXES -> assertThat(check.check()) + .hasSizeLessThanOrEqualTo(1); // TODO skip runtime checks after https://github.com/mfvanek/pg-index-health/issues/456 + + default -> assertThat(check.check()) + .as(check.getDiagnostic().name()) + .isEmpty(); + } + }); + } +} diff --git a/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/controllers/TimeControllerTest.java b/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/controllers/TimeControllerTest.java index 514a5359..3f626f44 100644 --- a/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/controllers/TimeControllerTest.java +++ b/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/controllers/TimeControllerTest.java @@ -47,7 +47,7 @@ class TimeControllerTest extends TestBase { @Autowired private Clock clock; @Autowired - private NamedParameterJdbcTemplate jdbcTemplate; + private NamedParameterJdbcTemplate namedParameterJdbcTemplate; @BeforeAll void setUpKafkaConsumer() { @@ -64,7 +64,7 @@ void tearDownKafkaConsumer() { @BeforeEach void cleanUpDatabase() { - jdbcTemplate.getJdbcTemplate().execute("truncate table otel_demo.storage"); + jdbcTemplate.execute("truncate table otel_demo.storage"); } @SneakyThrows @@ -111,14 +111,14 @@ void spanShouldBeReportedInLogs(@Nonnull final CapturedOutput output) { .until(() -> countRecordsInTable() >= 1L); assertThat(output.getAll()) .contains("Received record: " + received.value() + " with traceId " + traceId); - final String messageFromDb = jdbcTemplate.queryForObject("select message from otel_demo.storage where trace_id = :traceId", + final String messageFromDb = namedParameterJdbcTemplate.queryForObject("select message from otel_demo.storage where trace_id = :traceId", Map.of("traceId", traceId), String.class); assertThat(messageFromDb) .isEqualTo(received.value()); } private long countRecordsInTable() { - final Long queryResult = jdbcTemplate.getJdbcTemplate().queryForObject("select count(*) from otel_demo.storage", Long.class); + final Long queryResult = jdbcTemplate.queryForObject("select count(*) from otel_demo.storage", Long.class); return Objects.requireNonNullElse(queryResult, 0L); } } diff --git a/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/support/KafkaInitializer.java b/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/support/KafkaInitializer.java index 240d72ba..4302bbd9 100644 --- a/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/support/KafkaInitializer.java +++ b/spring-boot-3-demo-app/src/test/java/io/github/mfvanek/spring/boot3/test/support/KafkaInitializer.java @@ -16,7 +16,7 @@ public class KafkaInitializer implements ApplicationContextInitializer { - private static final DockerImageName IMAGE = DockerImageName.parse("postgres:16.2"); + private static final DockerImageName IMAGE = DockerImageName.parse("postgres:16.4"); private static final Network NETWORK = Network.newNetwork(); private static final PostgreSQLContainer CONTAINER = new PostgreSQLContainer<>(IMAGE); diff --git a/spring-boot-3-demo-app/src/test/resources/application-test.yml b/spring-boot-3-demo-app/src/test/resources/application-test.yml index c088383e..e69de29b 100644 --- a/spring-boot-3-demo-app/src/test/resources/application-test.yml +++ b/spring-boot-3-demo-app/src/test/resources/application-test.yml @@ -1,4 +0,0 @@ -spring: - jdbc: - template: - query-timeout: 1 # 1 second