Skip to content

Commit

Permalink
Support Testcontainers URL (#516)
Browse files Browse the repository at this point in the history
* Support Testcontainers URL

* Add demo app with Testcontainers URL
  • Loading branch information
mfvanek authored Nov 28, 2024
1 parent 77fc7a8 commit e5b6727
Show file tree
Hide file tree
Showing 13 changed files with 203 additions and 6 deletions.
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ findProject(":spring-boot-integration:kotlin-demo-app")?.name = "kotlin-demo-app
include("pg-index-health-bom")
include("pg-index-health-logger")
include("pg-index-health-core")
include("spring-boot-integration:postgres-tc-url-demo-app")
findProject(":spring-boot-integration:postgres-tc-url-demo-app")?.name = "postgres-tc-url-demo-app"
2 changes: 1 addition & 1 deletion spring-boot-integration/console-demo-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ ext["assertj.version"] = libs.versions.assertj.get()
ext["junit-jupiter.version"] = libs.versions.junit.get()

dependencies {
implementation(project(":spring-boot-integration:pg-index-health-test-starter"))
implementation(libs.spring.boot.starter.root)

testImplementation(libs.spring.boot.starter.test)
testImplementation(project(":spring-boot-integration:pg-index-health-test-starter"))
}
2 changes: 1 addition & 1 deletion spring-boot-integration/h2-demo-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ ext["assertj.version"] = libs.versions.assertj.get()
ext["junit-jupiter.version"] = libs.versions.junit.get()

dependencies {
implementation(project(":spring-boot-integration:pg-index-health-test-starter"))
implementation(libs.spring.boot.starter.data.jdbc)

runtimeOnly("com.h2database:h2")

testImplementation(libs.spring.boot.starter.test)
testImplementation(project(":spring-boot-integration:pg-index-health-test-starter"))
}
2 changes: 1 addition & 1 deletion spring-boot-integration/kotlin-demo-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ ext["junit-jupiter.version"] = libs.versions.junit.get()

dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation(project(":spring-boot-integration:pg-index-health-test-starter"))
implementation(project(":pg-index-health-testing"))
implementation(libs.spring.boot.starter.data.jdbc)
implementation(platform(libs.testcontainers.bom))
Expand All @@ -28,6 +27,7 @@ dependencies {
runtimeOnly(libs.postgresql)

testImplementation(libs.spring.boot.starter.test)
testImplementation(project(":spring-boot-integration:pg-index-health-test-starter"))

detektPlugins(libs.detekt.formatting)
detektPlugins(libs.detekt.libraries)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Objects;
import javax.annotation.Nullable;
import javax.sql.DataSource;

/**
Expand Down Expand Up @@ -58,7 +59,7 @@ public class DatabaseStructureHealthAutoConfiguration {
public PgConnection pgConnection(@Qualifier("dataSource") final DataSource dataSource,
@Value("${spring.datasource.url:#{null}}") final String databaseUrl) {
final PgHost host;
if (Objects.isNull(databaseUrl) || databaseUrl.isBlank()) {
if (needToGetUrlFromMetaData(databaseUrl)) {
try (Connection connection = dataSource.getConnection()) {
host = PgHostImpl.ofUrl(connection.getMetaData().getURL());
} catch (SQLException ex) {
Expand All @@ -69,4 +70,10 @@ public PgConnection pgConnection(@Qualifier("dataSource") final DataSource dataS
}
return PgConnectionImpl.of(dataSource, host);
}

private static boolean needToGetUrlFromMetaData(@Nullable final String databaseUrl) {
return Objects.isNull(databaseUrl) ||
databaseUrl.isBlank() ||
databaseUrl.startsWith(DatabaseStructureHealthCondition.TESTCONTAINERS_PG_URL_PREFIX);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@
*/
public class DatabaseStructureHealthCondition extends SpringBootCondition {

/**
* The URL prefix used in Testcontainers to initialize PostgreSQL containers.
* <p>
* Testcontainers provides a special JDBC URL format that allows for on-the-fly creation and management
* of PostgreSQL database containers during tests. This prefix is part of the JDBC URL and signals
* Testcontainers to handle the lifecycle of the container automatically.
* </p>
*
* @see <a href="https://java.testcontainers.org/modules/databases/jdbc/">Testcontainers JDBC Support</a>
*/
static final String TESTCONTAINERS_PG_URL_PREFIX = "jdbc:tc:postgresql:";

private static final String ORIGINAL_PG_URL_PREFIX = "jdbc:postgresql://";
private static final String PROPERTY_NAME = "spring.datasource.url";

/**
Expand All @@ -34,7 +47,7 @@ public ConditionOutcome getMatchOutcome(final ConditionContext context, final An
final ConditionMessage.Builder message = ConditionMessage.forCondition("pg.index.health.test PostgreSQL condition");
final String jdbcUrl = getJdbcUrl(context);
if (jdbcUrl != null && !jdbcUrl.isBlank()) {
if (jdbcUrl.startsWith("jdbc:postgresql://")) {
if (jdbcUrl.startsWith(ORIGINAL_PG_URL_PREFIX) || jdbcUrl.startsWith(TESTCONTAINERS_PG_URL_PREFIX)) {
return ConditionOutcome.match(message.foundExactly("found PostgreSQL connection " + jdbcUrl));
}
return ConditionOutcome.noMatch(message.notAvailable("not PostgreSQL connection"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,22 @@ void withDataSourceAndWrongConnectionString() {
});
}

@Test
void withDataSourceAndTestcontainersConnectionString() throws SQLException {
try (Connection connectionMock = Mockito.mock(Connection.class)) {
setMocks(connectionMock);

assertWithTestConfig()
.withPropertyValues("spring.datasource.url=jdbc:tc:postgresql:17.2:///test")
.withInitializer(AutoConfigurationTestBase::initialize)
.run(context -> {
assertThatBeansPresent(context);
assertThatBeansAreNotNullBean(context);
assertThatPgConnectionIsValid(context);
});
}
}

@Test
void shouldNotCreateAutoConfigurationWithDisabledProperty() {
assertWithTestConfig()
Expand Down
2 changes: 1 addition & 1 deletion spring-boot-integration/postgres-demo-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ ext["assertj.version"] = libs.versions.assertj.get()
ext["junit-jupiter.version"] = libs.versions.junit.get()

dependencies {
implementation(project(":spring-boot-integration:pg-index-health-test-starter"))
implementation(project(":pg-index-health-testing"))
implementation(libs.spring.boot.starter.data.jdbc)
implementation(platform(libs.testcontainers.bom))
Expand All @@ -20,6 +19,7 @@ dependencies {
runtimeOnly(libs.postgresql)

testImplementation(libs.spring.boot.starter.test)
testImplementation(project(":spring-boot-integration:pg-index-health-test-starter"))
}

lombok {
Expand Down
21 changes: 21 additions & 0 deletions spring-boot-integration/postgres-tc-url-demo-app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
plugins {
id("pg-index-health.java-application")
alias(libs.plugins.spring.boot.gradlePlugin)
alias(libs.plugins.spring.dependency.management)
}

ext["commons-lang3.version"] = libs.versions.commons.lang3.get()
ext["assertj.version"] = libs.versions.assertj.get()
// ext["mockito.version"] = libs.versions.mockito.get()
ext["junit-jupiter.version"] = libs.versions.junit.get()

dependencies {
implementation(platform(libs.testcontainers.bom))
implementation("org.testcontainers:postgresql")
implementation(libs.spring.boot.starter.data.jdbc)

runtimeOnly(libs.postgresql)

testImplementation(libs.spring.boot.starter.test)
testImplementation(project(":spring-boot-integration:pg-index-health-test-starter"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2019-2024. Ivan Vakhrushev and others.
* https://github.com/mfvanek/pg-index-health
*
* This file is a part of "pg-index-health" - a Java library for
* analyzing and maintaining indexes health in PostgreSQL databases.
*
* Licensed under the Apache License 2.0
*/

package io.github.mfvanek.pg.spring.postgres.tc.url;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PostgresTestcontainersUrlDemoApplication {

/**
* Demo application with PostgreSQL datasource.
*/
public static void main(final String[] args) {
SpringApplication.run(PostgresTestcontainersUrlDemoApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
debug: false

spring:
main:
banner-mode: off
datasource:
url: jdbc:tc:postgresql:17.2:///demo_for_pg_index_health_starter
driverClassName: org.testcontainers.jdbc.ContainerDatabaseDriver
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2019-2024. Ivan Vakhrushev and others.
* https://github.com/mfvanek/pg-index-health
*
* This file is a part of "pg-index-health" - a Java library for
* analyzing and maintaining indexes health in PostgreSQL databases.
*
* Licensed under the Apache License 2.0
*/

package io.github.mfvanek.pg.spring.postgres.tc.url;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;

@ExtendWith(OutputCaptureExtension.class)
class PostgresTestcontainersUrlDemoApplicationRunTest {

@Test
void applicationShouldRun(final CapturedOutput output) {
assertThatCode(() -> PostgresTestcontainersUrlDemoApplication.main(new String[]{}))
.doesNotThrowAnyException();
assertThat(output.getAll())
.contains("Starting PostgresTestcontainersUrlDemoApplication using Java")
.contains("Container is started (JDBC URL: jdbc:postgresql://localhost:")
.contains("Started PostgresTestcontainersUrlDemoApplication in");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2019-2024. Ivan Vakhrushev and others.
* https://github.com/mfvanek/pg-index-health
*
* This file is a part of "pg-index-health" - a Java library for
* analyzing and maintaining indexes health in PostgreSQL databases.
*
* Licensed under the Apache License 2.0
*/

package io.github.mfvanek.pg.spring.postgres.tc.url;

import com.zaxxer.hikari.HikariDataSource;
import io.github.mfvanek.pg.connection.PgConnection;
import io.github.mfvanek.pg.core.checks.common.DatabaseCheckOnHost;
import io.github.mfvanek.pg.core.checks.common.Diagnostic;
import io.github.mfvanek.pg.model.dbobject.DbObject;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ActiveProfiles;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@ActiveProfiles("test")
@SpringBootTest
class PostgresTestcontainersUrlDemoApplicationTest {

@Autowired
private ApplicationContext applicationContext;

@Autowired
private Environment environment;

@Autowired
private List<DatabaseCheckOnHost<? extends DbObject>> checks;

@Test
void contextLoadsAndDoesNotContainPgIndexHealthBeans() {
assertThat(applicationContext.getBean("dataSource"))
.isInstanceOf(HikariDataSource.class);

assertThat(applicationContext.getBean("pgConnection"))
.isInstanceOf(PgConnection.class);

assertThat(environment.getProperty("spring.datasource.url"))
.isNotBlank()
.isEqualTo("jdbc:tc:postgresql:17.2:///demo_for_pg_index_health_starter");
}

@Test
void checksShouldWork() {
assertThat(checks)
.hasSameSizeAs(Diagnostic.values());

checks.stream()
.filter(DatabaseCheckOnHost::isStatic)
.forEach(c -> {
assertThat(c.check())
.as(c.getDiagnostic().name())
.isEmpty();

assertThat(c.getHost().getPgUrl())
.startsWith("jdbc:postgresql://localhost:")
.endsWith("/test?loggerLevel=OFF");
});
}
}

0 comments on commit e5b6727

Please sign in to comment.