Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.gradle
build
release.sh
.env.release
40 changes: 39 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,17 @@ subprojects {
from(project.the<SourceSetContainer>()["main"].allSource)
}

val javadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
from(tasks.named("javadoc"))
}

extensions.configure<PublishingExtension>("publishing") {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
artifact(sourcesJar.get())
artifact(javadocJar.get())

pom {
name.set("Spring Test Containers")
Expand Down Expand Up @@ -81,7 +87,8 @@ subprojects {
repositories {
mavenLocal()
maven {
url = layout.buildDirectory.dir("staging-deploy").get().asFile.toURI()
name = "staging"
url = rootProject.layout.buildDirectory.dir("staging-deploy").get().asFile.toURI()
}
}
}
Expand All @@ -106,3 +113,34 @@ jreleaser {
}
}
}

val installGitHooks by tasks.registering {
doLast {
val gitDir = File(rootDir, ".git")
val hooksDir = File(gitDir, "hooks")
val hookSource = File(rootDir, "scripts/git-hooks/pre-commit")
val hookTarget = File(hooksDir, "pre-commit")

if (!hookSource.exists()) {
println("No pre-commit script found at ${hookSource.path}. Skipping hook installation.")
return@doLast
}

if (!gitDir.exists()) {
println("Not a Git repository. Skipping Git hook installation.")
return@doLast
}

println("Installing Git pre-commit hook...")
hookSource.copyTo(hookTarget, overwrite = true)
hookTarget.setExecutable(true)
println("✅ Git hook installed at: ${hookTarget.path}")
}
}

// Automatically install Git hooks on project evaluation
if (System.getenv("CI") != "true") {
gradle.projectsEvaluated {
tasks.findByName("build")?.dependsOn("installGitHooks")
}
}
14 changes: 0 additions & 14 deletions git-hooks/install-hooks.sh

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,55 @@
import io.flowinquiry.testcontainers.jdbc.SpringAwareJdbcContainerProvider;
import org.testcontainers.containers.MySQLContainer;

/**
* MySQL-specific implementation of the {@link SpringAwareJdbcContainerProvider}.
*
* <p>This class provides support for MySQL database containers in the Spring TestContainers
* framework. It creates and manages a {@link MySQLContainer} instance, which is a TestContainers
* implementation for MySQL databases.
*
* <p>This provider is automatically discovered by the {@link
* io.flowinquiry.testcontainers.jdbc.JdbcContainerProviderFactory} using Java's ServiceLoader
* mechanism when a test class is annotated with {@code @EnableMySQL}.
*
* <p>The provider handles:
*
* <ul>
* <li>Creating a MySQL container with the specified Docker image and version
* <li>Starting and stopping the container
* <li>Integrating the container with Spring's environment configuration
* </ul>
*
* @see SpringAwareJdbcContainerProvider
* @see MySQLContainer
* @see io.flowinquiry.testcontainers.jdbc.EnableMySQL
*/
public class MySqlContainerProvider extends SpringAwareJdbcContainerProvider {

/**
* Returns the type of database managed by this provider.
*
* <p>This implementation returns {@link Rdbms#MYSQL}, indicating that this provider supports
* MySQL databases.
*
* @return {@link Rdbms#MYSQL} as the supported database type
*/
@Override
public Rdbms getType() {
return Rdbms.MYSQL;
}

/**
* Creates a new MySQL database container.
*
* <p>This method creates a {@link MySQLContainer} instance configured with the Docker image and
* version specified in the {@code @EnableMySQL} annotation on the test class.
*
* <p>The container is created but not started. The {@link #start()} method must be called to
* start the container before it can be used.
*
* @return a new {@link MySQLContainer} instance
*/
@Override
protected MySQLContainer<?> createJdbcDatabaseContainer() {
return new MySQLContainer<>(dockerImage + ":" + version);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,55 @@
import org.testcontainers.containers.JdbcDatabaseContainer;
import org.testcontainers.containers.PostgreSQLContainer;

/**
* PostgreSQL-specific implementation of the {@link SpringAwareJdbcContainerProvider}.
*
* <p>This class provides support for PostgreSQL database containers in the Spring TestContainers
* framework. It creates and manages a {@link PostgreSQLContainer} instance, which is a
* TestContainers implementation for PostgreSQL databases.
*
* <p>This provider is automatically discovered by the {@link
* io.flowinquiry.testcontainers.jdbc.JdbcContainerProviderFactory} using Java's ServiceLoader
* mechanism when a test class is annotated with {@code @EnablePostgreSQL}.
*
* <p>The provider handles:
*
* <ul>
* <li>Creating a PostgreSQL container with the specified Docker image and version
* <li>Starting and stopping the container
* <li>Integrating the container with Spring's environment configuration
* </ul>
*
* @see SpringAwareJdbcContainerProvider
* @see PostgreSQLContainer
* @see io.flowinquiry.testcontainers.jdbc.EnablePostgreSQL
*/
public class PostgreSqlContainerProvider extends SpringAwareJdbcContainerProvider {

/**
* Returns the type of database managed by this provider.
*
* <p>This implementation returns {@link Rdbms#POSTGRESQL}, indicating that this provider supports
* PostgreSQL databases.
*
* @return {@link Rdbms#POSTGRESQL} as the supported database type
*/
@Override
public Rdbms getType() {
return Rdbms.POSTGRESQL;
}

/**
* Creates a new PostgreSQL database container.
*
* <p>This method creates a {@link PostgreSQLContainer} instance configured with the Docker image
* and version specified in the {@code @EnablePostgreSQL} annotation on the test class.
*
* <p>The container is created but not started. The {@link #start()} method must be called to
* start the container before it can be used.
*
* @return a new {@link PostgreSQLContainer} instance
*/
@Override
protected JdbcDatabaseContainer<?> createJdbcDatabaseContainer() {
return new PostgreSQLContainer<>(dockerImage + ":" + version);
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,45 @@

import java.util.ServiceLoader;

/**
* Factory class for creating and configuring JDBC container providers.
*
* <p>This factory is responsible for discovering and initializing the appropriate {@link
* JdbcContainerProvider} implementation based on the database type specified in the {@link
* EnableJdbcContainer} annotation.
*
* <p>The factory uses Java's {@link ServiceLoader} mechanism to discover available provider
* implementations. It selects the provider that matches the requested database type, initializes it
* with the specified Docker image and version, and prepares it for use in tests.
*
* <p>This class is primarily used by the {@link JdbcExtension} to create container providers for
* test classes annotated with database-specific annotations like {@code @EnablePostgreSQL} or
* {@code @EnableMySQL}.
*
* @see JdbcContainerProvider
* @see EnableJdbcContainer
* @see ServiceLoader
*/
public class JdbcContainerProviderFactory {

/**
* Creates and initializes a JDBC container provider for the specified configuration.
*
* <p>This method:
*
* <ol>
* <li>Discovers all available {@link JdbcContainerProvider} implementations using the {@link
* ServiceLoader} mechanism
* <li>Selects the provider that supports the database type specified in the {@link
* EnableJdbcContainer} annotation
* <li>Initializes the provider with the specified Docker image and version
* <li>Creates the JDBC database container
* </ol>
*
* @param enableJdbcContainer the annotation containing the database configuration
* @return a fully initialized JDBC container provider ready for use in tests
* @throws IllegalStateException if no provider is found for the specified database type
*/
public static JdbcContainerProvider getProvider(EnableJdbcContainer enableJdbcContainer) {
SpringAwareJdbcContainerProvider provider =
(SpringAwareJdbcContainerProvider)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,45 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A JUnit 5 extension that manages JDBC database containers for integration testing.
*
* <p>This extension automatically starts and stops database containers based on annotations present
* on the test class. It works with meta-annotations that are themselves annotated with {@link
* EnableJdbcContainer}, such as {@code @EnablePostgreSQL}, {@code @EnableMySQL}, etc.
*
* <p>The extension handles the lifecycle of database containers:
*
* <ul>
* <li>Before all tests: Detects database configuration from annotations and starts the
* appropriate container
* <li>After all tests: Stops the container and cleans up resources
* </ul>
*
* <p>Usage example:
*
* <pre>{@code
* @EnablePostgreSQL
* class MyIntegrationTest {
* // Test methods that require a PostgreSQL database
* }
* }</pre>
*/
public class JdbcExtension implements BeforeAllCallback, AfterAllCallback {

private static final Logger log = LoggerFactory.getLogger(JdbcExtension.class);

private JdbcContainerProvider provider;

/**
* Called before all tests in the current test class.
*
* <p>This method detects the database configuration from annotations on the test class, creates
* the appropriate container provider, and starts the database container if needed. If a container
* for this test class is already running, it reuses the existing container.
*
* @param context the extension context for the test class
*/
@Override
public void beforeAll(ExtensionContext context) {
Class<?> testClass = context.getRequiredTestClass();
Expand All @@ -33,6 +66,14 @@ public void beforeAll(ExtensionContext context) {
}
}

/**
* Called after all tests in the current test class have completed.
*
* <p>This method stops the database container if it was started by this extension and removes the
* container reference from the registry to allow proper cleanup.
*
* @param context the extension context for the test class
*/
@Override
public void afterAll(ExtensionContext context) {
if (provider != null) {
Expand Down Expand Up @@ -88,6 +129,19 @@ private Annotation findNearestAnnotationWith(
return null;
}

/**
* Builds a resolved JDBC container configuration from a source annotation and its
* meta-annotation.
*
* <p>This method extracts configuration values (version and dockerImage) from the source
* annotation and combines them with the database type (rdbms) from the meta-annotation to create
* a complete {@link EnableJdbcContainer} configuration.
*
* @param sourceAnnotation the source annotation containing version and dockerImage values
* @param meta the meta-annotation containing the database type (rdbms)
* @return a resolved {@link EnableJdbcContainer} configuration
* @throws IllegalStateException if reflection fails to extract values from the source annotation
*/
private EnableJdbcContainer buildResolvedJdbcConfig(
Annotation sourceAnnotation, EnableJdbcContainer meta) {
try {
Expand Down
Loading