Skip to content

Commit

Permalink
added mysql support
Browse files Browse the repository at this point in the history
  • Loading branch information
lpandzic committed Jan 2, 2024
1 parent 51d93bc commit a5b74b5
Show file tree
Hide file tree
Showing 15 changed files with 413 additions and 0 deletions.
90 changes: 90 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ Usual use cases include:
* [Local development](#MSSQLLocalDevelopment)
* [Docker image version](#MSSQLDockerImage)
* [Initialization script](#MSSQLInitScript)
* [MySQL](#MySQL)
* [Tests](#MySQLTests)
* [Local development](#MySQLLocalDevelopment)
* [Docker image version](#MySQLDockerImage)
* [Initialization script](#MySQLInitScript)
* [PostgreSQL](#PostgreSQL)
* [Tests](#PostgreSQLTests)
* [Local development](#PostgreSQLLocalDevelopment)
Expand Down Expand Up @@ -165,6 +170,91 @@ To add an SQL script with which the container will be initialized with add the f
testcontainers.mssql.init-script: db/init-script.sql
```

<a id="MySQL"></a>
### MySQL

<a id="MySQLTests"></a>
#### Tests:

Include the dependency:

```xml
<dependency>
<groupId>com.infobip</groupId>
<artifactId>infobip-mysql-testcontainers-spring-boot-starter</artifactId>
<version>${infobip-mysql-testcontainers-spring-boot-starter.version}</version>
<scope>test</scope>
</dependency>
```

Configuration:

```yaml
spring:
datasource:
url: jdbc:mysql://<host>:<port>/FooBarDb
username: test
password: test
```

Logical database is automatically created.
Container IP address is resolved based on running host, meaning on local machine `<host>` will resolve to `localhost`
while inside Docker placeholder will resolve to `containerIp`.
When `<port>` placeholder is used, container will be mapped on random port and automatically substituted.

<a id="MySQLLocalDevelopment"></a>
#### Local Development:

Add the following profile:

```xml
<profiles>
<profile>
<id>development</id>
<dependencies>
<dependency>
<groupId>com.infobip</groupId>
<artifactId>infobip-mysql-testcontainers-spring-boot-starter</artifactId>
<version>${infobip-mysql-testcontainers-spring-boot-starter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
</profiles>
```

Before starting the application locally, activate development profile:

![profile.png](profile.png)

and update your local configuration (e.g. application-development.yaml):

```yaml
spring:
datasource:
url: jdbc:mysql://<host>:<port>/FooBarDb_test_${user.name}
username: test
password: test
```

<a id="MySQLDockerImage"></a>
#### Docker image:

To change the docker image used simply add the following property (e.g. in yaml):

```yaml
testcontainers.mysql.docker.image: mysql:5.7.34
```

<a id="MySQLInitScript"></a>
#### Initialization script

To add an SQL script with which the container will be initialized with add the following property (e.g. in yaml):

```yaml
testcontainers.mysql.init-script: db/init-script.sql
```

<a id="PostgreSQL"></a>
### PostgreSQL

Expand Down
49 changes: 49 additions & 0 deletions infobip-mysql-testcontainers-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.infobip</groupId>
<artifactId>infobip-testcontainers-spring-boot-starter</artifactId>
<version>4.2.1-SNAPSHOT</version>
</parent>

<artifactId>infobip-mysql-testcontainers-spring-boot-starter</artifactId>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>infobip-testcontainers-common</artifactId>
<version>${project.version}</version>
</dependency>

<!-- LIBS -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>

<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>


<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<version>${testcontainers.version}</version>
</dependency>

<!--TEST-->
<dependency>
<groupId>com.infobip</groupId>
<artifactId>infobip-testcontainers-test-common</artifactId>
<scope>test</scope>
<version>${project.version}</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.infobip.testcontainers.spring.mysql;

import com.infobip.testcontainers.InitializerBase;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.StringUtils;

import java.util.Optional;

public class MySQLContainerInitializer extends InitializerBase<MySQLContainerWrapper> {

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
var environment = applicationContext.getEnvironment();
var dataSourceUrlPropertyName = Optional.ofNullable(
environment.getProperty("testcontainers.mysql.datasource.url.property.name"))
.orElse("spring.datasource.url");
var dataSourceUrl = environment.getProperty(dataSourceUrlPropertyName);

if (StringUtils.isEmpty(dataSourceUrl)) {
throw new IllegalStateException("URL for test-container is null or empty.");
}

var database = dataSourceUrl.substring(dataSourceUrl.lastIndexOf("/") + 1);
var wrapper = Optional.ofNullable(environment.getProperty("testcontainers.mysql.docker.image"))
.map(imageName -> new MySQLContainerWrapper(database, imageName))
.orElseGet(() -> new MySQLContainerWrapper(database));
var container = handleReusable(wrapper);

Optional.ofNullable(environment.getProperty("testcontainers.mysql.init-script"))
.ifPresent(container::withInitScript);

resolveStaticPort(dataSourceUrl, GENERIC_URL_WITH_PORT_GROUP_PATTERN)
.ifPresent(staticPort -> bindPort(container, staticPort, MySQLContainerWrapper.MYSQL_PORT));

start(container);

var url = replaceHostAndPortPlaceholders(dataSourceUrl, container, MySQLContainerWrapper.MYSQL_PORT);
var values = TestPropertyValues.of(dataSourceUrlPropertyName + "=" + url);

values.applyTo(applicationContext);

registerContainerAsBean(applicationContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.infobip.testcontainers.spring.mysql;

public class MySQLContainerWrapper extends org.testcontainers.containers.MySQLContainer<MySQLContainerWrapper> {

public MySQLContainerWrapper(String databaseName) {
this(databaseName, IMAGE + ":" + DEFAULT_TAG);
}

public MySQLContainerWrapper(String databaseName, String dockerImageName) {
super(dockerImageName);
withDatabaseName(databaseName);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.context.ApplicationContextInitializer=com.infobip.testcontainers.spring.mysql.MySQLContainerInitializer
org.springframework.context.ApplicationListener=com.infobip.testcontainers.spring.mysql.MySQLContainerInitializer
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.infobip.testcontainers.spring.mysql;

import com.infobip.testcontainers.TestBase;
import lombok.AllArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.springframework.test.context.ActiveProfiles;

import java.sql.*;

import static org.assertj.core.api.BDDAssertions.then;

@AllArgsConstructor
@ActiveProfiles("test")
class MySQLContainerInitializerTest extends TestBase {

private final DataSourceProperties properties;

@Test
void shouldCreateContainer() {
// given
JdbcTemplate jdbcTemplate = new JdbcTemplate(new SimpleDriverDataSource(
getDriver(properties.getUrl()),
properties.getUrl(),
properties.getUsername(),
properties.getPassword()));

// when
String actual = jdbcTemplate.queryForObject("SELECT database();", String.class);

// then
then(actual).isEqualTo("TestDatabase");
}

private Driver getDriver(String jdbcUrl) {
try {
return DriverManager.getDriver(jdbcUrl);
} catch (SQLException e) {
throw new IllegalArgumentException(e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.infobip.testcontainers.spring.mysql;

import com.infobip.testcontainers.TestBase;
import lombok.AllArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.springframework.test.context.ActiveProfiles;

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;

import static org.assertj.core.api.BDDAssertions.then;

@AllArgsConstructor
@ActiveProfiles("init-script")
class MySQLContainerInitializerWithInitScriptTest extends TestBase {

private final DataSourceProperties properties;

@Test
void shouldCreateContainerWithInitScript() {
// given
JdbcTemplate jdbcTemplate = new JdbcTemplate(new SimpleDriverDataSource(
getDriver(properties.getUrl()),
properties.getUrl(),
properties.getUsername(),
properties.getPassword()));

// when
String actual = jdbcTemplate.queryForObject("SELECT name FROM test_table LIMIT 1", String.class);

// then
then(actual).isEqualTo("Test");
}

private Driver getDriver(String jdbcUrl) {
try {
return DriverManager.getDriver(jdbcUrl);
} catch (SQLException e) {
throw new IllegalArgumentException(e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.infobip.testcontainers.spring.mysql;

import com.infobip.testcontainers.ReusableTestBase;
import com.infobip.testcontainers.spring.mysql.MySQLContainerWrapper;
import lombok.AllArgsConstructor;
import org.junit.jupiter.api.Test;
import org.springframework.test.context.ActiveProfiles;

import static org.assertj.core.api.BDDAssertions.then;

@AllArgsConstructor
@ActiveProfiles("reusable")
class MySQLContainerInitializerWithReusableTest extends ReusableTestBase {

private final MySQLContainerWrapper wrapper;
private final int port = MySQLContainerWrapper.MYSQL_PORT;

@Test
void shouldReuseContainer() {
// given
var givenContainer = new MySQLContainerWrapper("TestDatabase");
givenContainer.withReuse(true);

// when
givenContainer.start();

// then
then(givenContainer.getMappedPort(port)).isEqualTo(wrapper.getMappedPort(port));
}

}
Loading

0 comments on commit a5b74b5

Please sign in to comment.