Skip to content

Commit

Permalink
Merge pull request #285 from zonkyio/upgrade-test-suits
Browse files Browse the repository at this point in the history
Fix compatibility with Flyway 10.17.1 and above
  • Loading branch information
tomix26 authored Nov 25, 2024
2 parents 4428bd1 + 27dedbf commit ac8e3db
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 26 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ The primary goal of this project is to make it easier to write Spring-powered in
## Supported Integrations

* Supports both `Spring` and `Spring Boot` frameworks
* Spring `4.3.8` - `6.1.x`
* Spring Boot `1.4.6` - `3.2.x`
* Spring `4.3.8` - `6.2.x`
* Spring Boot `1.4.6` - `3.4.x`
* Supports multiple different databases
* [PostgreSQL](#postgresql), [MSSQL](#microsoft-sql-server), [MySQL](#mysql), [MariaDB](#mariadb), [H2](#h2), [HSQLDB](#hsqldb), [Derby](#derby)
* Supports multiple database providers
Expand Down
30 changes: 17 additions & 13 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ ext {
[name: 'no_sb', liquibase: 'default']
]],
[name: 'postgres', versions: [
[name: '12', postgres: '12.18', 'zonky-postgres': '12.18.0', opentable: 'default', yandex: 'default'],
[name: '13', postgres: '13.14', 'zonky-postgres': '13.14.0', opentable: 'default', yandex: 'default'],
[name: '14', postgres: '14.11', 'zonky-postgres': '14.11.0', opentable: 'default', yandex: 'default'],
[name: '15', postgres: '15.6', 'zonky-postgres': '15.6.0', opentable: 'default', yandex: 'default'],
[name: '16', postgres: '16.2', 'zonky-postgres': '16.2.0', opentable: 'default', yandex: 'default']
[name: '12', postgres: '12.22', 'zonky-postgres': '12.22.0', opentable: 'default', yandex: 'default'],
[name: '13', postgres: '13.18', 'zonky-postgres': '13.18.0', opentable: 'default', yandex: 'default'],
[name: '14', postgres: '14.15', 'zonky-postgres': '14.15.0', opentable: 'default', yandex: 'default'],
[name: '15', postgres: '15.10', 'zonky-postgres': '15.10.0', opentable: 'default', yandex: 'default'],
[name: '16', postgres: '16.6', 'zonky-postgres': '16.6.0', opentable: 'default', yandex: 'default'],
[name: '17', postgres: '17.2', 'zonky-postgres': '17.2.0', opentable: 'default', yandex: 'default']
]],
[name: 'mssql', versions: [
[name: '2017', 'mssql': '2017-latest', 'mssql-driver': 'default'],
Expand Down Expand Up @@ -102,16 +103,19 @@ if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {

testSuites.find { it.name == 'flyway' }.versions += [
[name: '9.9.0', flyway: '9.9.0', 'flyway-test': '9.5.0', spring: '6.0.14', 'spring-boot': '3.0.13', 'zonky-postgres': 'default'],
[name: '9.16.3', flyway: '9.16.3', 'flyway-test': '9.5.0', spring: '6.0.18', 'spring-boot': '3.1.10', 'zonky-postgres': 'default'],
[name: '9.22.3', flyway: '9.22.3', 'flyway-test': '9.5.0', spring: '6.1.5', 'spring-boot': '3.2.4', 'zonky-postgres': 'default'],
[name: '10.0.1', flyway: '10.0.1', 'flyway-test': '10.0.0', spring: '6.1.5', 'spring-boot': '3.2.4', 'zonky-postgres': 'default'],
[name: '10.11.0', flyway: '10.5.0', 'flyway-test': '10.0.0', spring: '6.1.5', 'spring-boot': '3.2.4', 'zonky-postgres': 'default']
[name: '9.16.3', flyway: '9.16.3', 'flyway-test': '9.5.0', spring: '6.0.21', 'spring-boot': '3.1.12', 'zonky-postgres': 'default'],
[name: '9.22.3', flyway: '9.22.3', 'flyway-test': '9.5.0', spring: '6.1.15', 'spring-boot': '3.2.12', 'zonky-postgres': 'default'],
[name: '10.10.0', flyway: '10.10.0', 'flyway-test': '10.0.0', spring: '6.1.15', 'spring-boot': '3.3.6', 'zonky-postgres': 'default'],
[name: '10.20.1', flyway: '10.20.1', 'flyway-test': '10.0.0', spring: '6.2.0', 'spring-boot': '3.4.0', 'zonky-postgres': 'default'],
[name: '11.0.0', flyway: '11.0.0', 'flyway-test': '10.0.0', spring: '6.2.0', 'spring-boot': '3.4.0', 'zonky-postgres': 'default']
]

testSuites.find { it.name == 'liquibase' }.versions += [
[name: '4.17.2', liquibase: '4.17.2', spring: '6.0.14', 'spring-boot': '3.0.13'],
[name: '4.20.0', liquibase: '4.20.0', spring: '6.0.18', 'spring-boot': '3.1.10'],
[name: '4.24.0', liquibase: '4.24.0', spring: '6.1.5', 'spring-boot': '3.2.4']
[name: '4.20.0', liquibase: '4.20.0', spring: '6.0.21', 'spring-boot': '3.1.12'],
[name: '4.24.0', liquibase: '4.24.0', spring: '6.1.15', 'spring-boot': '3.2.12'],
[name: '4.27.0', liquibase: '4.27.0', spring: '6.1.15', 'spring-boot': '3.3.6'],
[name: '4.29.2', liquibase: '4.29.2', spring: '6.2.0', 'spring-boot': '3.4.0']
]
}

Expand Down Expand Up @@ -258,7 +262,7 @@ project(':embedded-database-spring-test') {
api 'org.testcontainers:mysql:1.18.3'
api 'org.testcontainers:mariadb:1.18.3'

optImplementation 'io.zonky.test:embedded-postgres:2.0.7'
optImplementation 'io.zonky.test:embedded-postgres:2.1.0'
optImplementation 'com.opentable.components:otj-pg-embedded:0.13.4'
optImplementation 'ru.yandex.qatools.embed:postgresql-embedded:2.10'

Expand Down Expand Up @@ -319,7 +323,7 @@ project(':embedded-database-spring-test') {
"testRuntimeClasspath_${suite.name}_${version.name}" {
extendsFrom testRuntimeClasspath

if (version.flyway != null && version.flyway.startsWith('10.')) {
if (version.flyway != null && (version.flyway.startsWith('10.') || version.flyway.startsWith('11.'))) {
dependencies.add(project.dependencies.create("org.flywaydb:flyway-database-postgresql:${version.flyway}"))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.zonky.test.db.flyway;

import com.google.common.collect.ImmutableList;
import org.aopalliance.intercept.Interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.resolver.MigrationResolver;
Expand Down Expand Up @@ -113,8 +114,7 @@ public Collection<ResolvedMigration> getMigrations() {
if (flywayVersion.isGreaterThanOrEqualTo("9")) {
return invokeMethod(resolver, "resolveMigrations", config);
} else if (flywayVersion.isGreaterThanOrEqualTo("5.2")) {
Class<?> contextType = ClassUtils.forName("org.flywaydb.core.api.resolver.Context", classLoader);
Object contextInstance = ProxyFactory.getProxy(contextType, (MethodInterceptor) invocation ->
Object contextInstance = createMock("org.flywaydb.core.api.resolver.Context", (MethodInterceptor) invocation ->
"getConfiguration".equals(invocation.getMethod().getName()) ? config : invocation.proceed());
return invokeMethod(resolver, "resolveMigrations", contextInstance);
} else {
Expand All @@ -126,7 +126,17 @@ public Collection<ResolvedMigration> getMigrations() {
}

private MigrationResolver createMigrationResolver(Flyway flyway) throws ClassNotFoundException {
if (flywayVersion.isGreaterThanOrEqualTo("8")) {
if (flywayVersion.isGreaterThanOrEqualTo("10.17.1")) {
Object executor = getField(flyway, "flywayExecutor");
Object providers = invokeMethod(executor, "createResourceAndClassProviders", true);
Object resourceProvider = getField(providers, "left");
Object classProvider = getField(providers, "right");
Object sqlScript = createMock("org.flywaydb.core.internal.sqlscript.SqlScript", (MethodInterceptor) invocation -> false);
Object sqlScriptFactory = createMock("org.flywaydb.core.internal.sqlscript.SqlScriptFactory", (MethodInterceptor) invocation -> sqlScript);
Object sqlScriptExecutorFactory = createMock("org.flywaydb.core.internal.sqlscript.SqlScriptExecutorFactory");
Object parsingContext = invokeConstructor("org.flywaydb.core.internal.parser.ParsingContext");
return invokeMethod(executor, "createMigrationResolver", resourceProvider, classProvider, sqlScriptExecutorFactory, sqlScriptFactory, parsingContext, null);
} else if (flywayVersion.isGreaterThanOrEqualTo("8")) {
Object executor = getField(flyway, "flywayExecutor");
Object providers = invokeMethod(executor, "createResourceAndClassProviders", true);
Object resourceProvider = getField(providers, "left");
Expand Down Expand Up @@ -493,4 +503,9 @@ private static Object createMock(String className) throws ClassNotFoundException
Class<?> proxyInterface = ClassUtils.forName(className, classLoader);
return ProxyFactory.getProxy(proxyInterface, (MethodInterceptor) invocation -> null);
}

private static Object createMock(String className, Interceptor interceptor) throws ClassNotFoundException {
Class<?> proxyInterface = ClassUtils.forName(className, classLoader);
return ProxyFactory.getProxy(proxyInterface, interceptor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.opentable.db.postgres.embedded.EmbeddedPostgres;
import io.zonky.test.category.PostgresTestSuite;
import io.zonky.test.db.AutoConfigureEmbeddedDatabase;
import io.zonky.test.support.TestSocketUtils;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
Expand All @@ -28,7 +29,6 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.SocketUtils;

import javax.sql.DataSource;
import java.sql.SQLException;
Expand All @@ -49,7 +49,7 @@ static class Config {

@Bean
public Integer randomPort() {
return SocketUtils.findAvailableTcpPort();
return TestSocketUtils.findAvailableTcpPort();
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.zonky.test.category.PostgresTestSuite;
import io.zonky.test.db.AutoConfigureEmbeddedDatabase;
import io.zonky.test.db.postgres.embedded.EmbeddedPostgres;
import io.zonky.test.support.TestSocketUtils;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
Expand All @@ -28,7 +29,6 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.SocketUtils;

import javax.sql.DataSource;
import java.sql.SQLException;
Expand All @@ -49,7 +49,7 @@ static class Config {

@Bean
public Integer randomPort() {
return SocketUtils.findAvailableTcpPort();
return TestSocketUtils.findAvailableTcpPort();
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.zonky.test.db.preparer.DatabasePreparer;
import io.zonky.test.db.provider.support.BlockingDatabaseWrapper;
import io.zonky.test.db.support.TestDatabasePreparer;
import io.zonky.test.support.TestSocketUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -29,7 +30,6 @@
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.util.SocketUtils;

import javax.sql.DataSource;
import java.sql.SQLException;
Expand Down Expand Up @@ -93,7 +93,7 @@ public void testGetDatabase() throws Exception {

@Test
public void testDatabaseCustomizers() throws Exception {
int randomPort = SocketUtils.findAvailableTcpPort();
int randomPort = TestSocketUtils.findAvailableTcpPort();
when(databaseCustomizers.getIfAvailable()).thenReturn(Collections.singletonList(builder -> builder.setPort(randomPort)));

DatabasePreparer preparer = TestDatabasePreparer.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.zonky.test.db.preparer.DatabasePreparer;
import io.zonky.test.db.provider.support.BlockingDatabaseWrapper;
import io.zonky.test.db.support.TestDatabasePreparer;
import io.zonky.test.support.TestSocketUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -29,7 +30,6 @@
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.util.SocketUtils;

import javax.sql.DataSource;
import java.sql.SQLException;
Expand Down Expand Up @@ -93,7 +93,7 @@ public void testGetDatabase() throws Exception {

@Test
public void testDatabaseCustomizers() throws Exception {
int randomPort = SocketUtils.findAvailableTcpPort();
int randomPort = TestSocketUtils.findAvailableTcpPort();
when(databaseCustomizers.getIfAvailable()).thenReturn(Collections.singletonList(builder -> builder.setPort(randomPort)));

DatabasePreparer preparer = TestDatabasePreparer.empty();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.zonky.test.support;

import org.springframework.util.Assert;

import javax.net.ServerSocketFactory;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.Random;

public class TestSocketUtils {

private static final int PORT_RANGE_MIN = 1024;
private static final int PORT_RANGE_MAX = 65535;
private static final int PORT_RANGE_PLUS_ONE = PORT_RANGE_MAX - PORT_RANGE_MIN + 1;
private static final int MAX_ATTEMPTS = 1_000;

private static final Random random = new Random(System.nanoTime());

private TestSocketUtils() {}

public static int findAvailableTcpPort() {
int candidatePort;
int searchCounter = 0;
do {
Assert.state(++searchCounter <= MAX_ATTEMPTS, () -> String.format(
"Could not find an available TCP port in the range [%d, %d] after %d attempts",
PORT_RANGE_MIN, PORT_RANGE_MAX, MAX_ATTEMPTS));
candidatePort = PORT_RANGE_MIN + random.nextInt(PORT_RANGE_PLUS_ONE);
}
while (!isPortAvailable(candidatePort));

return candidatePort;
}

private static boolean isPortAvailable(int port) {
try {
ServerSocket serverSocket = ServerSocketFactory.getDefault()
.createServerSocket(port, 1, InetAddress.getByName("localhost"));
serverSocket.close();
return true;
}
catch (Exception ex) {
return false;
}
}
}

0 comments on commit ac8e3db

Please sign in to comment.