Skip to content

Commit 786daa7

Browse files
committed
feat(jooq): Allow to choose Flyway migrations location
fix #11
1 parent 8d50139 commit 786daa7

File tree

10 files changed

+213
-33
lines changed

10 files changed

+213
-33
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ void setUp(@Named("my_catalog_1") DataSource dataSource1,
130130

131131
This extension depends on a [DatasourceExtension](https://rocket.i-run.si/javadoc/fr/irun/testy/jooq/DatasourceExtension.html) and runs a [Flyway](https://flywaydb.org/) migration on the related DB catalog.
132132

133-
The SQL scripts shall be located into `db.migration.<catalog>` in the classpath, where `<catalog>` is the name of DataSource catalog. The names of the SQL files shall match [Flyway naming convention](https://flywaydb.org/documentation/migrations#naming).
133+
By default, the SQL scripts have to be located into `db.migration.<catalog>` in the classpath, where `<catalog>` is the name of DataSource catalog. The names of the SQL files shall match [Flyway naming convention](https://flywaydb.org/documentation/migrations#naming).
134134

135135
The SQL scripts are run **before all the test methods**. They are expected to be used to create the database schema.
136136

testy-beat-box/src/main/java/fr/irun/testy/beat/extensions/WithRabbitMock.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
* <li>Can declare many queues with related exchanges</li>
4545
* <li>Builds a {@link MockedSender} and a {@link MockedReceiver} to simplify the mocking of the queues.</li>
4646
* </ul>
47-
* <p>
47+
* <br>
4848
* Usage :
4949
* <pre style="code">
5050
* private static final String QUEUE_1 = "test-queue-1";
@@ -66,7 +66,7 @@
6666
* .append(withRabbit)
6767
* .register();
6868
* </pre>
69-
* <p>
69+
* <br>
7070
* Example to test a "listener" class.
7171
* A "listener" is expected to:
7272
* <ul>
@@ -76,7 +76,7 @@
7676
* <li>Reply another message on the reply queue</li>
7777
* <li>The {@link MockedSender} can be used to simplify the sending of messages on a queue.</li>
7878
* </ul>
79-
* <p>
79+
* <br>
8080
* <pre style="code">
8181
* {@literal @}Test
8282
* void should_consume_queue_and_reply_message(MockedSender mockedSender, ObjectMapper objectMapper) {
@@ -92,21 +92,21 @@
9292
* assertThat(actualResponse).isEqualTo("expected message replied by tested listener");
9393
* }
9494
* </pre>
95-
* <p>
95+
* <br>
9696
* Assert example to test an "emitter" class.
9797
* An "emitter" is expected to:
9898
* <ul>
9999
* <li>Send a message on the queue/exchange</li>
100100
* <li>Treat the response.</li>
101101
* </ul>
102-
* <p>
102+
* <br>
103103
* Note that:
104104
* <ul>
105105
* <li>An {@link MockedReceiver} can be injected to the test.</li>
106106
* <li>It can consume a defined number of messages on a queue and reply defined responses.</li>
107107
* <li>The method {@link MockedReceiver.MockedConsumerBuilder#start()} returns all the requests consumed from the queue.</li>
108108
* </ul>
109-
* <p>
109+
* <br>
110110
* <pre style="code">
111111
* {@literal @}Test
112112
* void should_emit_message_and_manage_response(MockedReceiver mockedReceiver,

testy-jooq-box/src/main/java/fr/irun/testy/jooq/WithDatabaseLoaded.java

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,66 @@
11
package fr.irun.testy.jooq;
22

3+
import lombok.AllArgsConstructor;
34
import org.flywaydb.core.Flyway;
5+
import org.flywaydb.core.api.Location;
6+
import org.flywaydb.core.api.configuration.ClassicConfiguration;
47
import org.junit.jupiter.api.extension.BeforeAllCallback;
58
import org.junit.jupiter.api.extension.BeforeEachCallback;
69
import org.junit.jupiter.api.extension.ExtensionContext;
710

11+
import javax.annotation.Nullable;
812
import javax.sql.DataSource;
913
import java.util.Objects;
14+
import java.util.Optional;
1015

16+
/**
17+
* <p>This extension depends on a {@link DatasourceExtension} and runs a <a href="https://flywaydb.org/">Flyway</a>
18+
* migration on the related DB catalog.</p>
19+
*
20+
* <p>By default, the SQL scripts have to be located into {@code db.migration.<catalog>} in the classpath, where {@code <catalog>}
21+
* is the name of DataSource catalog. The names of the SQL files shall match
22+
* <a href="https://flywaydb.org/documentation/migrations#naming">Flyway naming convention</a>.</p>
23+
*
24+
* <p>The SQL scripts are run **before all the test methods**. They are expected to be used to create the database schema.</p>
25+
*
26+
* <pre><code>
27+
* private static final WithInMemoryDatasource wDataSource = WithInMemoryDatasource.builder()
28+
* .setCatalog("my_catalog")
29+
* .build();
30+
*
31+
* // SQL files shall be located in classpath:db.migration.my_catalog
32+
* private static final WithDatabaseLoaded wDatabaseLoaded = WithDatabaseLoaded.builder()
33+
* .setDatasourceExtension(wDataSource)
34+
* .build();
35+
*
36+
* {@literal @}RegisterExtension
37+
* static final ChainedExtension chain = ChainedExtension
38+
* .outer(wDataSource)
39+
* .append(wDatabaseLoaded)
40+
* .register();
41+
* </code></pre>
42+
*/
43+
@AllArgsConstructor
1144
public final class WithDatabaseLoaded implements BeforeAllCallback, BeforeEachCallback {
1245
private static final String P_LOADED = "dbLoaded_";
1346

1447
private final DatasourceExtension wDatasource;
15-
16-
private WithDatabaseLoaded(DatasourceExtension dataSourceProvider) {
17-
this.wDatasource = dataSourceProvider;
18-
}
48+
@Nullable
49+
private final Location location;
1950

2051
@Override
2152
public void beforeAll(ExtensionContext context) {
2253
String catalog = getContextCatalog(context);
2354
DataSource dataSource = Objects.requireNonNull(wDatasource.getDataSource(context),
2455
"DataSource not found in context Store !");
2556

57+
Location migrationsLocation = Optional.ofNullable(location)
58+
.orElseGet(() -> new Location("classpath:db/migration/" + catalog));
2659
Flyway flyway = Flyway.configure()
2760
.dataSource(dataSource)
2861
.schemas(catalog)
2962
.placeholderReplacement(false)
30-
.locations("classpath:db/migration/" + catalog)
63+
.locations(migrationsLocation)
3164
.load();
3265
flyway.clean();
3366
flyway.migrate();
@@ -55,17 +88,65 @@ public static WithDatabaseLoadedBuilder builder() {
5588
return new WithDatabaseLoadedBuilder();
5689
}
5790

91+
/**
92+
* Builder for {@link WithDatabaseLoaded}
93+
*/
5894
public static class WithDatabaseLoadedBuilder {
5995
private DatasourceExtension wDatasource;
96+
private Location location = null;
6097

98+
/**
99+
* <p>Allow to link the {@link DatasourceExtension} with the {@link WithDatabaseLoaded}. The Flyway migrations
100+
* was applied to the {@link DataSource} created by this extension.</p>
101+
* <p>This setter is mandatory.</p>
102+
*
103+
* @param wDatasource The linked {@link DataSource} extension
104+
* @return The current builder
105+
*/
61106
public WithDatabaseLoadedBuilder setDatasourceExtension(DatasourceExtension wDatasource) {
62107
this.wDatasource = wDatasource;
63108
return this;
64109
}
65110

111+
/**
112+
* <p>Allow to set the migrations location. To indicate Flyway where to found SQL files.</p>
113+
*
114+
* <p>By default, the {@link WithDatabaseLoaded} use {@code db/migration/catalog}, where catalog was the
115+
* schema given by the {@link DataSource} context</p>
116+
*
117+
* <p>This method was not compatible with {@link #useFlywayDefaultLocation()}</p>
118+
*
119+
* @param location The migration files location.
120+
* @return The current builder
121+
*/
122+
public WithDatabaseLoadedBuilder setMigrationsLocation(String location) {
123+
this.location = new Location(location);
124+
return this;
125+
}
126+
127+
/**
128+
* <p>Use the Flyway default migrations location instead a migration with catalog name.</p>
129+
*
130+
* <p>By default, the {@link WithDatabaseLoaded} use {@code db/migration/catalog}, where catalog was the
131+
* schema given by the {@link DataSource} context</p>
132+
*
133+
* <p>This method was not compatible with {@link #setMigrationsLocation(String)}</p>
134+
*
135+
* @return The current builder
136+
*/
137+
public WithDatabaseLoadedBuilder useFlywayDefaultLocation() {
138+
this.location = new ClassicConfiguration().getLocations()[0];
139+
return this;
140+
}
141+
142+
/**
143+
* Build the {@link WithDatabaseLoaded} extension
144+
*
145+
* @return The extension
146+
*/
66147
public WithDatabaseLoaded build() {
67148
Objects.requireNonNull(wDatasource, "A DataSource extension was mandatory !");
68-
return new WithDatabaseLoaded(wDatasource);
149+
return new WithDatabaseLoaded(wDatasource, location);
69150
}
70151
}
71152
}

testy-jooq-box/src/test/java/fr/irun/testy/jooq/WithDatabaseLoadedTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ class WithDatabaseLoadedTest {
2222
static WithDatabaseLoaded wDbLoaded = WithDatabaseLoaded.builder()
2323
.setDatasourceExtension(wDs).build();
2424

25+
@RegisterExtension
26+
static WithDatabaseLoaded wDbLoadedLocation = WithDatabaseLoaded.builder()
27+
.setMigrationsLocation("db/migration/dummy_legacy")
28+
.setDatasourceExtension(wDs).build();
29+
2530
@Test
2631
void should_get_data_form_loaded_db(DataSource ds) throws SQLException {
2732
List<String> actuals = new ArrayList<>();
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package fr.irun.testy.jooq;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.junit.jupiter.api.extension.RegisterExtension;
5+
6+
import javax.sql.DataSource;
7+
import java.sql.Connection;
8+
import java.sql.ResultSet;
9+
import java.sql.SQLException;
10+
import java.sql.Statement;
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
16+
class WithDatabaseLoadedWithLocationTest {
17+
@RegisterExtension
18+
static WithInMemoryDatasource wDs = WithInMemoryDatasource.builder()
19+
.setCatalog("dummy").build();
20+
21+
@RegisterExtension
22+
static WithDatabaseLoaded wDbLoadedLocation = WithDatabaseLoaded.builder()
23+
.setMigrationsLocation("db/migration/dummy_legacy")
24+
.setDatasourceExtension(wDs).build();
25+
26+
@Test
27+
void should_get_data_form_loaded_db(DataSource ds) throws SQLException {
28+
List<String> actuals = new ArrayList<>();
29+
try (Connection conn = ds.getConnection();
30+
Statement stmt = conn.createStatement()) {
31+
ResultSet rs = stmt.executeQuery("SELECT * FROM GUNGAN");
32+
33+
while (rs.next()) {
34+
actuals.add(rs.getString(1) + " " + rs.getString(2));
35+
}
36+
rs.close();
37+
}
38+
39+
assertThat(actuals).contains("Jar Jar Binks");
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package fr.irun.testy.jooq;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.junit.jupiter.api.extension.RegisterExtension;
5+
6+
import javax.sql.DataSource;
7+
import java.sql.Connection;
8+
import java.sql.ResultSet;
9+
import java.sql.SQLException;
10+
import java.sql.Statement;
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
16+
class WithDatabaseLoadedWithoutCatalogTest {
17+
@RegisterExtension
18+
static WithInMemoryDatasource wDs = WithInMemoryDatasource.builder().build();
19+
20+
@RegisterExtension
21+
static WithDatabaseLoaded wDbLoaded = WithDatabaseLoaded.builder()
22+
.setDatasourceExtension(wDs)
23+
.useFlywayDefaultLocation()
24+
.build();
25+
26+
@Test
27+
void should_get_data_form_loaded_db(DataSource ds) throws SQLException {
28+
List<String> actuals = new ArrayList<>();
29+
try (Connection conn = ds.getConnection();
30+
Statement stmt = conn.createStatement()) {
31+
ResultSet rs = stmt.executeQuery("SELECT * FROM JEDI");
32+
33+
while (rs.next()) {
34+
actuals.add(rs.getString(1) + " " + rs.getString(2));
35+
}
36+
rs.close();
37+
}
38+
39+
assertThat(actuals).contains("Obiwan Kenobi", "Dark Vador");
40+
}
41+
}

testy-jooq-box/src/test/java/fr/irun/testy/jooq/WithMultipleDataSourcesTest.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,28 @@
1616
class WithMultipleDataSourcesTest {
1717
private static final String NORA_CATALOG = "dummy_nora";
1818
private static final String LEGACY_CATALOG = "dummy_legacy";
19-
private static final String SQL_SELECT_ALL_JEDIS = "SELECT FIRST_NAME, LAST_NAME FROM JEDI";
19+
private static final String SQL_SELECT_ALL_GUNGANS = "SELECT FIRST_NAME, LAST_NAME FROM GUNGAN";
20+
private static final String SQL_SELECT_ALL_MASTERS = "SELECT FIRST_NAME, LAST_NAME FROM MASTER";
2021

21-
private static WithInMemoryDatasource wLegacyDatasource = WithInMemoryDatasource.builder()
22+
private final static WithInMemoryDatasource wLegacyDatasource = WithInMemoryDatasource.builder()
2223
.setCatalog(LEGACY_CATALOG)
2324
.setReferentialIntegrity(false)
2425
.build();
25-
private static WithDatabaseLoaded wLegacyDatabase = WithDatabaseLoaded.builder()
26+
private final static WithDatabaseLoaded wLegacyDatabase = WithDatabaseLoaded.builder()
2627
.setDatasourceExtension(wLegacyDatasource)
2728
.build();
28-
private static WithDslContext wLegacyContext = WithDslContext.builder()
29+
private final static WithDslContext wLegacyContext = WithDslContext.builder()
2930
.setDatasourceExtension(wLegacyDatasource)
3031
.setDialect(SQLDialect.H2)
3132
.build();
3233

33-
private static WithInMemoryDatasource wNoraDatasource = WithInMemoryDatasource.builder()
34+
private final static WithInMemoryDatasource wNoraDatasource = WithInMemoryDatasource.builder()
3435
.setCatalog(NORA_CATALOG)
3536
.build();
36-
private static WithDatabaseLoaded wNoraDatabase = WithDatabaseLoaded.builder()
37+
private final static WithDatabaseLoaded wNoraDatabase = WithDatabaseLoaded.builder()
3738
.setDatasourceExtension(wNoraDatasource)
3839
.build();
39-
private static WithDslContext wNoraContext = WithDslContext.builder()
40+
private final static WithDslContext wNoraContext = WithDslContext.builder()
4041
.setDatasourceExtension(wNoraDatasource)
4142
.setDialect(SQLDialect.H2)
4243
.build();
@@ -63,15 +64,15 @@ void shouldSetLegacyContext(@Named(LEGACY_CATALOG) DSLContext legacyContext,
6364
// Check data
6465
assertThat(legacyContext).isNotNull();
6566

66-
final Result<Record> actualLegacyResult = legacyContext.fetch(SQL_SELECT_ALL_JEDIS);
67+
final Result<Record> actualLegacyResult = legacyContext.fetch(SQL_SELECT_ALL_GUNGANS);
6768
assertThat(actualLegacyResult).isNotNull();
6869
assertThat(actualLegacyResult).isNotEmpty();
6970
assertThat(actualLegacyResult).hasSize(1);
7071

7172
final Record actualLegacyRecord = actualLegacyResult.get(0);
7273
assertThat(actualLegacyRecord).isNotNull();
73-
assertThat(actualLegacyRecord.get(0)).isEqualTo("Dark");
74-
assertThat(actualLegacyRecord.get(1)).isEqualTo("Vador");
74+
assertThat(actualLegacyRecord.get(0)).isEqualTo("Jar Jar");
75+
assertThat(actualLegacyRecord.get(1)).isEqualTo("Binks");
7576
}
7677

7778
@Test
@@ -84,7 +85,7 @@ void shouldSetNoraContext(@Named(NORA_CATALOG) DSLContext noraContext,
8485
// Check data
8586
assertThat(noraContext).isNotNull();
8687

87-
final Result<Record> actualNoraResult = noraContext.fetch(SQL_SELECT_ALL_JEDIS);
88+
final Result<Record> actualNoraResult = noraContext.fetch(SQL_SELECT_ALL_MASTERS);
8889
assertThat(actualNoraResult).isNotNull();
8990
assertThat(actualNoraResult).isNotEmpty();
9091
assertThat(actualNoraResult).hasSize(1);
@@ -107,23 +108,23 @@ void shouldSetBothContexts(@Named(LEGACY_CATALOG) DSLContext legacyContext,
107108
// Check legacy data
108109
assertThat(legacyContext).isNotNull();
109110

110-
final Result<Record> actualLegacyResult = legacyContext.fetch(SQL_SELECT_ALL_JEDIS);
111+
final Result<Record> actualLegacyResult = legacyContext.fetch(SQL_SELECT_ALL_GUNGANS);
111112
assertThat(actualLegacyResult).isNotNull();
112113
assertThat(actualLegacyResult).isNotEmpty();
113114
assertThat(actualLegacyResult).hasSize(1);
114115

115116
final Record actualLegacyRecord = actualLegacyResult.get(0);
116117
assertThat(actualLegacyRecord).isNotNull();
117-
assertThat(actualLegacyRecord.get(0)).isEqualTo("Dark");
118-
assertThat(actualLegacyRecord.get(1)).isEqualTo("Vador");
118+
assertThat(actualLegacyRecord.get(0)).isEqualTo("Jar Jar");
119+
assertThat(actualLegacyRecord.get(1)).isEqualTo("Binks");
119120

120121
// Check nora catalog
121122
assertThat(noraCatalog).isEqualTo(NORA_CATALOG);
122123

123124
// Check nora data
124125
assertThat(noraContext).isNotNull();
125126

126-
final Result<Record> actualNoraResult = noraContext.fetch(SQL_SELECT_ALL_JEDIS);
127+
final Result<Record> actualNoraResult = noraContext.fetch(SQL_SELECT_ALL_MASTERS);
127128
assertThat(actualNoraResult).isNotNull();
128129
assertThat(actualNoraResult).isNotEmpty();
129130
assertThat(actualNoraResult).hasSize(1);

0 commit comments

Comments
 (0)