Skip to content

Commit 22642fc

Browse files
committed
#275 implementation of database initializer to populate the database
1 parent a40fb09 commit 22642fc

File tree

7 files changed

+284
-0
lines changed

7 files changed

+284
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,11 @@ zonky.test.database.provider=default # Provider to be used to creat
472472
zonky.test.database.refresh=never # Determines the refresh mode of the embedded database.
473473
zonky.test.database.replace=any # Determines what type of existing DataSource beans can be replaced.
474474

475+
zonky.test.database.init.script-locations= # Locations of the SQL scripts to apply to the database.
476+
zonky.test.database.init.continue-on-error=false # Whether initialization should continue when an error occurs.
477+
zonky.test.database.init.separator=; # Statement separator in the SQL scripts.
478+
zonky.test.database.init.encoding= # Encoding of the SQL scripts.
479+
475480
zonky.test.database.postgres.client.properties.*= # Additional PostgreSQL options used to configure the test data source.
476481
zonky.test.database.mssql.client.properties.*= # Additional MSSQL options used to configure the test data source.
477482
zonky.test.database.mysql.client.properties.*= # Additional MySQL options used to configure the test data source.

embedded-database-spring-test/src/main/java/io/zonky/test/db/config/EmbeddedDatabaseAutoConfiguration.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import io.zonky.test.db.flyway.FlywayDatabaseExtension;
2020
import io.zonky.test.db.flyway.FlywayPropertiesPostProcessor;
21+
import io.zonky.test.db.init.EmbeddedDatabaseInitializer;
22+
import io.zonky.test.db.init.ScriptDatabasePreparer;
2123
import io.zonky.test.db.liquibase.LiquibaseDatabaseExtension;
2224
import io.zonky.test.db.liquibase.LiquibasePropertiesPostProcessor;
2325
import io.zonky.test.db.provider.DatabaseProvider;
@@ -44,6 +46,9 @@
4446
import org.springframework.core.env.Environment;
4547
import org.springframework.util.ClassUtils;
4648

49+
import java.nio.charset.Charset;
50+
import java.util.Arrays;
51+
4752
@Configuration
4853
public class EmbeddedDatabaseAutoConfiguration implements BeanClassLoaderAware {
4954

@@ -274,6 +279,22 @@ public LiquibasePropertiesPostProcessor liquibasePropertiesPostProcessor() {
274279
return new LiquibasePropertiesPostProcessor();
275280
}
276281

282+
@Bean
283+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
284+
@ConditionalOnMissingBean(name = "embeddedDatabaseInitializer")
285+
public EmbeddedDatabaseInitializer embeddedDatabaseInitializer(Environment environment) {
286+
String[] scriptLocations = environment.getProperty("zonky.test.database.init.script-locations", String[].class);
287+
boolean continueOnError = environment.getProperty("zonky.test.database.init.continue-on-error", boolean.class, false);
288+
String separator = environment.getProperty("zonky.test.database.init.separator", ";");
289+
Charset encoding = environment.getProperty("zonky.test.database.init.encoding", Charset.class);
290+
291+
ScriptDatabasePreparer scriptPreparer = null;
292+
if (scriptLocations != null) {
293+
scriptPreparer = new ScriptDatabasePreparer(Arrays.asList(scriptLocations), continueOnError, separator, encoding);
294+
}
295+
return new EmbeddedDatabaseInitializer(scriptPreparer);
296+
}
297+
277298
private void checkDependency(String groupId, String artifactId, String className) {
278299
if (!ClassUtils.isPresent(className, classLoader)) {
279300
String dependencyName = String.format("%s:%s", groupId, artifactId);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.zonky.test.db.init;
18+
19+
import io.zonky.test.db.context.DatabaseContext;
20+
import org.springframework.beans.BeansException;
21+
import org.springframework.beans.factory.config.BeanPostProcessor;
22+
import org.springframework.core.Ordered;
23+
24+
public class EmbeddedDatabaseInitializer implements BeanPostProcessor, Ordered {
25+
26+
private final ScriptDatabasePreparer scriptPreparer;
27+
28+
public EmbeddedDatabaseInitializer(ScriptDatabasePreparer scriptPreparer) {
29+
this.scriptPreparer = scriptPreparer;
30+
}
31+
32+
@Override
33+
public int getOrder() {
34+
return Ordered.HIGHEST_PRECEDENCE + 10;
35+
}
36+
37+
@Override
38+
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
39+
if (bean instanceof DatabaseContext && scriptPreparer != null) {
40+
DatabaseContext databaseContext = (DatabaseContext) bean;
41+
databaseContext.apply(scriptPreparer);
42+
}
43+
return bean;
44+
}
45+
46+
@Override
47+
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
48+
return bean;
49+
}
50+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.zonky.test.db.init;
18+
19+
import com.google.common.base.MoreObjects;
20+
import io.zonky.test.db.preparer.DatabasePreparer;
21+
import org.springframework.core.io.Resource;
22+
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
23+
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
24+
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
25+
26+
import javax.sql.DataSource;
27+
import java.nio.charset.Charset;
28+
import java.sql.SQLException;
29+
import java.util.List;
30+
import java.util.Objects;
31+
32+
public class ScriptDatabasePreparer implements DatabasePreparer {
33+
34+
private final List<String> scriptLocations;
35+
private final boolean continueOnError;
36+
private final String separator;
37+
private final Charset encoding;
38+
39+
public ScriptDatabasePreparer(List<String> scriptLocations) {
40+
this.scriptLocations = scriptLocations;
41+
this.continueOnError = false;
42+
this.separator = ";";
43+
this.encoding = null;
44+
}
45+
46+
public ScriptDatabasePreparer(List<String> scriptLocations, boolean continueOnError, String separator, Charset encoding) {
47+
this.scriptLocations = scriptLocations;
48+
this.continueOnError = continueOnError;
49+
this.separator = separator;
50+
this.encoding = encoding;
51+
}
52+
53+
@Override
54+
public long estimatedDuration() {
55+
return 10;
56+
}
57+
58+
@Override
59+
public void prepare(DataSource dataSource) throws SQLException {
60+
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
61+
populator.setContinueOnError(continueOnError);
62+
populator.setSeparator(separator);
63+
if (encoding != null) {
64+
populator.setSqlScriptEncoding(encoding.name());
65+
}
66+
67+
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
68+
for (String scriptLocation : scriptLocations) {
69+
Resource resource = resolver.getResource(scriptLocation);
70+
populator.addScript(resource);
71+
}
72+
DatabasePopulatorUtils.execute(populator, dataSource);
73+
}
74+
75+
@Override
76+
public boolean equals(Object o) {
77+
if (this == o) return true;
78+
if (o == null || getClass() != o.getClass()) return false;
79+
ScriptDatabasePreparer that = (ScriptDatabasePreparer) o;
80+
return continueOnError == that.continueOnError
81+
&& Objects.equals(scriptLocations, that.scriptLocations)
82+
&& Objects.equals(separator, that.separator)
83+
&& Objects.equals(encoding, that.encoding);
84+
}
85+
86+
@Override
87+
public int hashCode() {
88+
return Objects.hash(scriptLocations, continueOnError, separator, encoding);
89+
}
90+
91+
@Override
92+
public String toString() {
93+
return MoreObjects.toStringHelper(this)
94+
.add("scriptLocation", scriptLocations)
95+
.add("continueOnError", continueOnError)
96+
.add("separator", separator)
97+
.add("encoding", encoding)
98+
.toString();
99+
}
100+
}

embedded-database-spring-test/src/main/resources/META-INF/spring-configuration-metadata.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
"name": "zonky.test.database",
55
"description": "Configuration properties for Zonky Embedded Database library."
66
},
7+
{
8+
"name": "zonky.test.database.init",
9+
"description": "Configuration properties to initialize the database."
10+
},
711
{
812
"name": "zonky.test.database.prefetching",
913
"description": "Configuration properties to configure prefetching of prepared databases."
@@ -70,6 +74,28 @@
7074
"description": "Determines what type of existing DataSource beans can be replaced.",
7175
"defaultValue": "any"
7276
},
77+
{
78+
"name": "zonky.test.database.init.script-locations",
79+
"type": "java.util.List<java.lang.String>",
80+
"description": "Locations of the SQL scripts to apply to the database."
81+
},
82+
{
83+
"name": "zonky.test.database.init.continue-on-error",
84+
"type": "java.lang.Boolean",
85+
"description": "Whether initialization should continue when an error occurs.",
86+
"defaultValue": false
87+
},
88+
{
89+
"name": "zonky.test.database.init.separator",
90+
"type": "java.lang.String",
91+
"description": "Statement separator in the SQL scripts.",
92+
"defaultValue": ";"
93+
},
94+
{
95+
"name": "zonky.test.database.init.encoding",
96+
"type": "java.nio.charset.Charset",
97+
"description": "Encoding of the SQL scripts."
98+
},
7399
{
74100
"name": "zonky.test.database.prefetching.thread-name-prefix",
75101
"type": "java.lang.String",
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.zonky.test.db;
18+
19+
import io.zonky.test.category.SpringTestSuite;
20+
import org.junit.After;
21+
import org.junit.Before;
22+
import org.junit.Test;
23+
import org.junit.experimental.categories.Category;
24+
import org.junit.runner.RunWith;
25+
import org.springframework.beans.factory.annotation.Autowired;
26+
import org.springframework.jdbc.core.JdbcTemplate;
27+
import org.springframework.test.context.TestPropertySource;
28+
import org.springframework.test.context.junit4.SpringRunner;
29+
import org.springframework.test.jdbc.JdbcTestUtils;
30+
31+
import javax.sql.DataSource;
32+
import java.util.List;
33+
import java.util.Map;
34+
35+
import static io.zonky.test.db.AutoConfigureEmbeddedDatabase.DatabaseType.POSTGRES;
36+
import static org.assertj.core.api.Assertions.assertThat;
37+
import static org.assertj.core.api.Assertions.entry;
38+
39+
@RunWith(SpringRunner.class)
40+
@Category(SpringTestSuite.class)
41+
@AutoConfigureEmbeddedDatabase(type = POSTGRES)
42+
@TestPropertySource(properties = {
43+
"zonky.test.database.init.script-locations=" +
44+
"/db/schema/init-schema.sql," +
45+
"/db/migration/V0001_1__create_person_table.sql," +
46+
"/db/migration/V0002_1__rename_surname_column.sql"
47+
})
48+
public class DatabaseInitializerIntegrationTest {
49+
50+
private static final String SQL_SELECT_PERSONS = "select * from test.person";
51+
52+
@Autowired
53+
private DataSource dataSource;
54+
55+
private JdbcTemplate jdbcTemplate;
56+
57+
@Before
58+
public void setUp() {
59+
jdbcTemplate = new JdbcTemplate(dataSource);
60+
jdbcTemplate.afterPropertiesSet();
61+
}
62+
63+
@After
64+
public void tearDown() {
65+
JdbcTestUtils.dropTables(jdbcTemplate, "test.person");
66+
}
67+
68+
@Test
69+
public void testScriptLocations() {
70+
assertThat(dataSource).isNotNull();
71+
72+
List<Map<String, Object>> persons = jdbcTemplate.queryForList(SQL_SELECT_PERSONS);
73+
assertThat(persons).isNotNull().hasSize(1);
74+
75+
Map<String, Object> person = persons.get(0);
76+
assertThat(person).containsExactly(
77+
entry("id", 1L),
78+
entry("first_name", "Dave"),
79+
entry("last_name", "Syer"));
80+
}
81+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
create schema if not exists test

0 commit comments

Comments
 (0)