Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package it.inaf.ia2.transfer.persistence;
import com.opentable.db.postgres.embedded.EmbeddedPostgres;
import com.opentable.db.postgres.embedded.PgBinaryResolver;
import com.opentable.db.postgres.embedded.UncompressBundleDirectoryResolver;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.sql.DataSource;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ScriptUtils;
/**
* Generates a DataSource that can be used for testing DAO classes. It loads an
* embedded Postgres database and fills it using the data from
* vospace-transfer-service repository (folder must exists; it location can be
* configured using the init_database_scripts_path in test.properties).
*/
@TestConfiguration
public class DataSourceConfig {
@Value("${init_database_scripts_path}")
private String scriptPath;
/**
* Using the prototype scope we are generating a different database in each
* test.
*/
@Bean
@Scope("prototype")
@Primary
public DataSource dataSource() throws Exception {
DataSource embeddedPostgresDS = EmbeddedPostgres.builder()
.setPgDirectoryResolver(new UncompressBundleDirectoryResolver(new CustomPostgresBinaryResolver()))
.start().getPostgresDatabase();
initDatabase(embeddedPostgresDS);
return embeddedPostgresDS;
}
private class CustomPostgresBinaryResolver implements PgBinaryResolver {
/**
* Loads specific embedded Postgres version.
*/
@Override
public InputStream getPgBinary(String system, String architecture) throws IOException {
ClassPathResource resource = new ClassPathResource(String.format("postgres-%s-%s.txz", system.toLowerCase(), architecture));
return resource.getInputStream();
}
}
/**
* Loads SQL scripts for database initialization from
* vospace-transfer-service repo directory.
*/
private void initDatabase(DataSource dataSource) throws Exception {
try ( Connection conn = dataSource.getConnection()) {
File currentDir = new File(DataSourceConfig.class.getClassLoader().getResource(".").getFile());
File scriptDir = currentDir.toPath().resolve(scriptPath).toFile().getCanonicalFile();
assertTrue(scriptDir.exists(), "DAO tests require " + scriptDir.getAbsolutePath() + " to exists.\n"
+ "Please clone the repository from https://www.ict.inaf.it/gitlab/vospace/vospace-file-catalog.git");
File[] scripts = scriptDir.listFiles(f -> f.getName().endsWith(".sql"));
Arrays.sort(scripts); // sort alphabetically
for (File script : scripts) {
ByteArrayResource scriptResource = replaceDollarQuoting(script.toPath());
ScriptUtils.executeSqlScript(conn, scriptResource);
}
ScriptUtils.executeSqlScript(conn, new ClassPathResource("test-data.sql"));
}
}
/**
* It seems that dollar quoting (used in UDF) is broken in JDBC. Replacing
* it with single quotes solves the problem. We replace the quoting here
* instead of inside the original files because dollar quoting provides a
* better visibility.
*/
private ByteArrayResource replaceDollarQuoting(Path sqlScriptPath) throws Exception {
String scriptContent = Files.readString(sqlScriptPath);
if (scriptContent.contains("$func$")) {
String func = extractFunctionDefinition(scriptContent);
String originalFunction = "$func$" + func + "$func$";
String newFunction = "'" + func.replaceAll("'", "''") + "'";
scriptContent = scriptContent.replace(originalFunction, newFunction);
}
return new ByteArrayResource(scriptContent.getBytes());
}
private String extractFunctionDefinition(String scriptContent) {
Pattern pattern = Pattern.compile("\\$func\\$(.*?)\\$func\\$", Pattern.DOTALL);
Matcher matcher = pattern.matcher(scriptContent);
if (matcher.find()) {
return matcher.group(1);
}
throw new IllegalArgumentException(scriptContent + " doesn't contain $func$");
}
}