Merge remote-tracking branch 'origin/master' into rel_8_7-tracking
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<java.version>17</java.version>
|
<java.version>17</java.version>
|
||||||
<hapi.fhir.jpa.server.starter.revision>1</hapi.fhir.jpa.server.starter.revision>
|
<hapi.fhir.jpa.server.starter.revision>1</hapi.fhir.jpa.server.starter.revision>
|
||||||
<clinical-reasoning.version>4.0.0</clinical-reasoning.version>
|
<clinical-reasoning.version>4.2.0</clinical-reasoning.version>
|
||||||
|
|
||||||
<!-- Plugins Versions -->
|
<!-- Plugins Versions -->
|
||||||
<maven.failsafe.version>3.5.4</maven.failsafe.version>
|
<maven.failsafe.version>3.5.4</maven.failsafe.version>
|
||||||
|
|||||||
@@ -3,21 +3,18 @@ package ca.uhn.fhir.jpa.starter;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchRestClientFactory;
|
|
||||||
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
|
import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.starter.common.TestContainerHelper;
|
||||||
import ca.uhn.fhir.jpa.starter.elastic.ElasticsearchBootSvcImpl;
|
import ca.uhn.fhir.jpa.starter.elastic.ElasticsearchBootSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.test.config.TestElasticsearchContainerHelper;
|
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
|
||||||
import co.elastic.clients.elasticsearch.indices.IndexSettings;
|
|
||||||
import jakarta.annotation.PreDestroy;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.DateTimeType;
|
import org.hl7.fhir.r4.model.DateTimeType;
|
||||||
@@ -26,85 +23,41 @@ import org.hl7.fhir.r4.model.Observation;
|
|||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.util.TestPropertyValues;
|
|
||||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||||
import org.springframework.context.ApplicationContextInitializer;
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.test.context.DynamicPropertySource;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.TestPropertySource;
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
|
||||||
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
||||||
import org.testcontainers.junit.jupiter.Container;
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
|
||||||
@ExtendWith(SpringExtension.class)
|
|
||||||
@Testcontainers
|
@Testcontainers
|
||||||
@ActiveProfiles("test")
|
@ActiveProfiles("test")
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class, ElasticsearchLastNR4IT.TestConfig.class}, properties =
|
@TestPropertySource(locations = "classpath:test-elasticsearch-lastn.yaml")
|
||||||
{
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {Application.class})
|
||||||
"spring.datasource.url=jdbc:h2:mem:dbr4",
|
|
||||||
"hapi.fhir.fhir_version=r4",
|
|
||||||
"hapi.fhir.lastn_enabled=true",
|
|
||||||
"hapi.fhir.store_resource_in_lucene_index_enabled=true",
|
|
||||||
"hapi.fhir.advanced_lucene_indexing=true",
|
|
||||||
"hapi.fhir.search_index_full_text_enabled=true",
|
|
||||||
"hapi.fhir.cr_enabled=false",
|
|
||||||
// Because the port is set randomly, we will set the rest_url using the Initializer.
|
|
||||||
// "elasticsearch.rest_url='http://localhost:9200'",
|
|
||||||
|
|
||||||
"spring.elasticsearch.uris=http://localhost:9200",
|
|
||||||
"spring.elasticsearch.username=elastic",
|
|
||||||
"spring.elasticsearch.password=changeme",
|
|
||||||
"spring.main.allow-bean-definition-overriding=true",
|
|
||||||
"spring.jpa.properties.hibernate.search.enabled=true",
|
|
||||||
"spring.jpa.properties.hibernate.search.backend.type=elasticsearch",
|
|
||||||
"spring.jpa.properties.hibernate.search.backend.hosts=localhost:9200",
|
|
||||||
"spring.jpa.properties.hibernate.search.backend.protocol=http",
|
|
||||||
"spring.jpa.properties.hibernate.search.backend.analysis.configurer=ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticsearchAnalysisConfigurer"
|
|
||||||
})
|
|
||||||
@ContextConfiguration(initializers = ElasticsearchLastNR4IT.Initializer.class)
|
|
||||||
class ElasticsearchLastNR4IT {
|
class ElasticsearchLastNR4IT {
|
||||||
private IGenericClient ourClient;
|
private IGenericClient ourClient;
|
||||||
private FhirContext ourCtx;
|
|
||||||
|
|
||||||
@Container
|
@Container
|
||||||
public static ElasticsearchContainer embeddedElastic = TestElasticsearchContainerHelper.getEmbeddedElasticSearch();
|
private static final ElasticsearchContainer ELASTICSEARCH = TestContainerHelper.newElasticsearchContainer()
|
||||||
|
// Set index defaults to handle HAPI FHIR's MAX_SUBSCRIPTION_RESULTS (50000)
|
||||||
|
.withEnv("indices.query.bool.max_clause_count", "50000");
|
||||||
|
|
||||||
|
@DynamicPropertySource
|
||||||
|
static void registerElasticsearchProperties(DynamicPropertyRegistry registry) {
|
||||||
|
TestContainerHelper.registerElasticsearchProperties(registry, ELASTICSEARCH);
|
||||||
|
// Also register spring.elasticsearch.uris for ElasticConfigCondition to enable ElasticsearchBootSvcImpl
|
||||||
|
registry.add("spring.elasticsearch.uris", () -> TestContainerHelper.getElasticsearchHttpUrl(ELASTICSEARCH));
|
||||||
|
}
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ElasticsearchBootSvcImpl myElasticsearchSvc;
|
private ElasticsearchBootSvcImpl myElasticsearchSvc;
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
public static void beforeClass() throws IOException {
|
|
||||||
//Given
|
|
||||||
ElasticsearchClient elasticsearchHighLevelRestClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient(
|
|
||||||
"http", embeddedElastic.getHost() + ":" + embeddedElastic.getMappedPort(9200), "", "");
|
|
||||||
|
|
||||||
/* As of 2023-08-10, HAPI FHIR sets SubscriptionConstants.MAX_SUBSCRIPTION_RESULTS to 50000
|
|
||||||
which is in excess of elastic's default max_result_window. If MAX_SUBSCRIPTION_RESULTS is changed
|
|
||||||
to a value <= 10000, the following will no longer be necessary. - dotasek
|
|
||||||
*/
|
|
||||||
|
|
||||||
elasticsearchHighLevelRestClient.indices().putTemplate(t->{
|
|
||||||
t.name("hapi_fhir_template");
|
|
||||||
t.indexPatterns("*");
|
|
||||||
t.settings(new IndexSettings.Builder().maxNgramDiff(50).maxResultWindow(50000).build());
|
|
||||||
return t;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
public void stop() {
|
|
||||||
embeddedElastic.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
@@ -119,9 +72,8 @@ class ElasticsearchLastNR4IT {
|
|||||||
Observation obs = new Observation();
|
Observation obs = new Observation();
|
||||||
obs.getSubject().setReferenceElement(id);
|
obs.getSubject().setReferenceElement(id);
|
||||||
String observationCode = "testobservationcode";
|
String observationCode = "testobservationcode";
|
||||||
String codeSystem = "http://testobservationcodesystem";
|
|
||||||
|
|
||||||
obs.getCode().addCoding().setCode(observationCode).setSystem(codeSystem);
|
obs.getCode().addCoding().setCode(observationCode).setSystem("http://testobservationcodesystem");
|
||||||
obs.setValue(new StringType(observationCode));
|
obs.setValue(new StringType(observationCode));
|
||||||
|
|
||||||
Date effectiveDtm = new GregorianCalendar().getTime();
|
Date effectiveDtm = new GregorianCalendar().getTime();
|
||||||
@@ -142,38 +94,11 @@ class ElasticsearchLastNR4IT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() throws IOException {
|
void beforeEach() {
|
||||||
|
FhirContext ctx = FhirContext.forR4();
|
||||||
ourCtx = FhirContext.forR4();
|
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
ctx.getRestfulClientFactory().setSocketTimeout((int) Duration.ofMinutes(20).toMillis());
|
||||||
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
|
ourClient = ctx.newRestfulGenericClient("http://localhost:" + port + "/fhir/");
|
||||||
String ourServerBase = "http://localhost:" + port + "/fhir/";
|
|
||||||
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
|
|
||||||
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class TestConfig {
|
|
||||||
@Bean
|
|
||||||
public ElasticsearchClient elasticsearchClient() throws IOException {
|
|
||||||
return ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient(
|
|
||||||
"http", embeddedElastic.getHost() + ":" + embeddedElastic.getMappedPort(9200), "", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Initializer
|
|
||||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(
|
|
||||||
ConfigurableApplicationContext configurableApplicationContext) {
|
|
||||||
// Since the port is dynamically generated, replace the URL with one that has the correct port
|
|
||||||
TestPropertyValues.of("spring.elasticsearch.uris=http://" + embeddedElastic.getHost() + ":" + embeddedElastic.getMappedPort(9200))
|
|
||||||
.applyTo(configurableApplicationContext.getEnvironment());
|
|
||||||
TestPropertyValues.of("spring.jpa.properties.hibernate.search.backend.hosts=" + embeddedElastic.getHost() +":" + embeddedElastic.getMappedPort(9200))
|
|
||||||
.applyTo(configurableApplicationContext.getEnvironment());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ca.uhn.fhir.jpa.starter;
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.starter.common.TestContainerHelper;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
@@ -33,48 +34,27 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||||||
class PostgresElasticsearchPatientIT {
|
class PostgresElasticsearchPatientIT {
|
||||||
|
|
||||||
@Container
|
@Container
|
||||||
private static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>("postgres:16-alpine")
|
private static final PostgreSQLContainer<?> POSTGRES = TestContainerHelper.newPostgresContainer();
|
||||||
.withDatabaseName("hapi")
|
|
||||||
.withUsername("fhiruser")
|
|
||||||
.withPassword("fhirpass");
|
|
||||||
|
|
||||||
@Container
|
@Container
|
||||||
private static final ElasticsearchContainer ELASTICSEARCH = new ElasticsearchContainer(
|
private static final ElasticsearchContainer ELASTICSEARCH = TestContainerHelper.newElasticsearchContainer();
|
||||||
"docker.elastic.co/elasticsearch/elasticsearch:8.11.0"
|
|
||||||
)
|
|
||||||
.withEnv("xpack.security.enabled", "false")
|
|
||||||
.withEnv("discovery.type", "single-node")
|
|
||||||
.withEnv("ES_JAVA_OPTS", "-Xms512m -Xmx512m");
|
|
||||||
|
|
||||||
@DynamicPropertySource
|
@DynamicPropertySource
|
||||||
static void registerDatasourceProperties(DynamicPropertyRegistry registry) {
|
static void registerDatasourceProperties(DynamicPropertyRegistry registry) {
|
||||||
// PostgreSQL configuration
|
TestContainerHelper.registerPostgresAndElasticsearchProperties(registry, POSTGRES, ELASTICSEARCH);
|
||||||
registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
|
|
||||||
registry.add("spring.datasource.username", POSTGRES::getUsername);
|
|
||||||
registry.add("spring.datasource.password", POSTGRES::getPassword);
|
|
||||||
registry.add("spring.datasource.driver-class-name", POSTGRES::getDriverClassName);
|
|
||||||
registry.add("spring.jpa.properties.hibernate.dialect", () -> "ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect");
|
|
||||||
|
|
||||||
// Elasticsearch configuration
|
|
||||||
registry.add("spring.jpa.properties.hibernate.search.backend.hosts", ELASTICSEARCH::getHttpHostAddress);
|
|
||||||
registry.add("spring.jpa.properties.hibernate.search.backend.protocol", () -> "http");
|
|
||||||
registry.add("spring.jpa.properties.hibernate.search.backend.username", () -> "");
|
|
||||||
registry.add("spring.jpa.properties.hibernate.search.backend.password", () -> "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
private IGenericClient ourClient;
|
private IGenericClient ourClient;
|
||||||
private FhirContext ourCtx;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() {
|
void beforeEach() {
|
||||||
ourCtx = FhirContext.forR4();
|
FhirContext ctx = FhirContext.forR4();
|
||||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||||
ourCtx.getRestfulClientFactory().setSocketTimeout((int) Duration.ofMinutes(20).toMillis());
|
ctx.getRestfulClientFactory().setSocketTimeout((int) Duration.ofMinutes(20).toMillis());
|
||||||
String ourServerBase = "http://localhost:" + port + "/fhir/";
|
ourClient = ctx.newRestfulGenericClient("http://localhost:" + port + "/fhir/");
|
||||||
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
|
|
||||||
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ca.uhn.fhir.jpa.starter;
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.starter.common.TestContainerHelper;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
@@ -32,33 +33,24 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||||||
class PostgresLucenePatientIT {
|
class PostgresLucenePatientIT {
|
||||||
|
|
||||||
@Container
|
@Container
|
||||||
private static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>("postgres:16-alpine")
|
private static final PostgreSQLContainer<?> POSTGRES = TestContainerHelper.newPostgresContainer();
|
||||||
.withDatabaseName("hapi")
|
|
||||||
.withUsername("fhiruser")
|
|
||||||
.withPassword("fhirpass");
|
|
||||||
|
|
||||||
@DynamicPropertySource
|
@DynamicPropertySource
|
||||||
static void registerDatasourceProperties(DynamicPropertyRegistry registry) {
|
static void registerDatasourceProperties(DynamicPropertyRegistry registry) {
|
||||||
registry.add("spring.datasource.url", POSTGRES::getJdbcUrl);
|
TestContainerHelper.registerPostgresProperties(registry, POSTGRES);
|
||||||
registry.add("spring.datasource.username", POSTGRES::getUsername);
|
|
||||||
registry.add("spring.datasource.password", POSTGRES::getPassword);
|
|
||||||
registry.add("spring.datasource.driver-class-name", POSTGRES::getDriverClassName);
|
|
||||||
registry.add("spring.jpa.properties.hibernate.dialect", () -> "ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
private IGenericClient ourClient;
|
private IGenericClient ourClient;
|
||||||
private FhirContext ourCtx;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() {
|
void beforeEach() {
|
||||||
ourCtx = FhirContext.forR4();
|
FhirContext ctx = FhirContext.forR4();
|
||||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||||
ourCtx.getRestfulClientFactory().setSocketTimeout((int) Duration.ofMinutes(20).toMillis());
|
ctx.getRestfulClientFactory().setSocketTimeout((int) Duration.ofMinutes(20).toMillis());
|
||||||
String ourServerBase = "http://localhost:" + port + "/fhir/";
|
ourClient = ctx.newRestfulGenericClient("http://localhost:" + port + "/fhir/");
|
||||||
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
|
|
||||||
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
package ca.uhn.fhir.jpa.starter.common;
|
||||||
|
|
||||||
|
import org.springframework.test.context.DynamicPropertyRegistry;
|
||||||
|
import org.testcontainers.containers.PostgreSQLContainer;
|
||||||
|
import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for creating and configuring Testcontainers used in integration tests.
|
||||||
|
* <p>
|
||||||
|
* This class provides factory methods for creating pre-configured containers and utility methods
|
||||||
|
* for registering container properties with Spring's {@link DynamicPropertyRegistry}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <h2>Usage Example:</h2>
|
||||||
|
* <pre>{@code
|
||||||
|
* @Testcontainers
|
||||||
|
* @SpringBootTest
|
||||||
|
* class MyIntegrationTest {
|
||||||
|
*
|
||||||
|
* @Container
|
||||||
|
* private static final PostgreSQLContainer<?> POSTGRES = TestContainerHelper.newPostgresContainer();
|
||||||
|
*
|
||||||
|
* @Container
|
||||||
|
* private static final ElasticsearchContainer ELASTICSEARCH = TestContainerHelper.newElasticsearchContainer();
|
||||||
|
*
|
||||||
|
* @DynamicPropertySource
|
||||||
|
* static void registerProperties(DynamicPropertyRegistry registry) {
|
||||||
|
* TestContainerHelper.registerPostgresProperties(registry, POSTGRES);
|
||||||
|
* TestContainerHelper.registerElasticsearchProperties(registry, ELASTICSEARCH);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public final class TestContainerHelper {
|
||||||
|
|
||||||
|
// Container image versions
|
||||||
|
private static final String POSTGRES_IMAGE = "postgres:16-alpine";
|
||||||
|
private static final String ELASTICSEARCH_IMAGE = "elasticsearch:8.19.10";
|
||||||
|
|
||||||
|
// Default PostgreSQL configuration
|
||||||
|
private static final String DEFAULT_DATABASE_NAME = "hapi";
|
||||||
|
private static final String DEFAULT_USERNAME = "fhiruser";
|
||||||
|
private static final String DEFAULT_PASSWORD = "fhirpass";
|
||||||
|
|
||||||
|
// Hibernate dialect for HAPI FHIR with PostgreSQL
|
||||||
|
private static final String HAPI_POSTGRES_DIALECT = "ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect";
|
||||||
|
|
||||||
|
private TestContainerHelper() {
|
||||||
|
// Utility class - prevent instantiation
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new PostgreSQL container with default HAPI FHIR configuration.
|
||||||
|
* <p>
|
||||||
|
* The container is configured with:
|
||||||
|
* <ul>
|
||||||
|
* <li>Image: postgres:16-alpine</li>
|
||||||
|
* <li>Database name: hapi</li>
|
||||||
|
* <li>Username: fhiruser</li>
|
||||||
|
* <li>Password: fhirpass</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return a new pre-configured PostgreSQL container
|
||||||
|
*/
|
||||||
|
public static PostgreSQLContainer<?> newPostgresContainer() {
|
||||||
|
return new PostgreSQLContainer<>(POSTGRES_IMAGE)
|
||||||
|
.withDatabaseName(DEFAULT_DATABASE_NAME)
|
||||||
|
.withUsername(DEFAULT_USERNAME)
|
||||||
|
.withPassword(DEFAULT_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Elasticsearch container with default configuration for HAPI FHIR.
|
||||||
|
* <p>
|
||||||
|
* The container is configured with:
|
||||||
|
* <ul>
|
||||||
|
* <li>Image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0</li>
|
||||||
|
* <li>Security disabled (xpack.security.enabled=false)</li>
|
||||||
|
* <li>Single-node discovery mode</li>
|
||||||
|
* <li>JVM heap: 512MB min/max</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return a new pre-configured Elasticsearch container
|
||||||
|
*/
|
||||||
|
public static ElasticsearchContainer newElasticsearchContainer() {
|
||||||
|
return new ElasticsearchContainer(ELASTICSEARCH_IMAGE)
|
||||||
|
.withEnv("xpack.security.enabled", "false")
|
||||||
|
.withEnv("discovery.type", "single-node")
|
||||||
|
.withEnv("ES_JAVA_OPTS", "-Xms512m -Xmx512m");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers PostgreSQL container properties with Spring's DynamicPropertyRegistry.
|
||||||
|
* <p>
|
||||||
|
* Registers the following properties:
|
||||||
|
* <ul>
|
||||||
|
* <li>spring.datasource.url</li>
|
||||||
|
* <li>spring.datasource.username</li>
|
||||||
|
* <li>spring.datasource.password</li>
|
||||||
|
* <li>spring.datasource.driver-class-name</li>
|
||||||
|
* <li>spring.jpa.properties.hibernate.dialect</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param registry the Spring dynamic property registry
|
||||||
|
* @param container the PostgreSQL container
|
||||||
|
*/
|
||||||
|
public static void registerPostgresProperties(DynamicPropertyRegistry registry, PostgreSQLContainer<?> container) {
|
||||||
|
registry.add("spring.datasource.url", container::getJdbcUrl);
|
||||||
|
registry.add("spring.datasource.username", container::getUsername);
|
||||||
|
registry.add("spring.datasource.password", container::getPassword);
|
||||||
|
registry.add("spring.datasource.driver-class-name", container::getDriverClassName);
|
||||||
|
registry.add("spring.jpa.properties.hibernate.dialect", () -> HAPI_POSTGRES_DIALECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers Elasticsearch container properties with Spring's DynamicPropertyRegistry.
|
||||||
|
* <p>
|
||||||
|
* Registers the following properties:
|
||||||
|
* <ul>
|
||||||
|
* <li>spring.jpa.properties.hibernate.search.backend.hosts</li>
|
||||||
|
* <li>spring.jpa.properties.hibernate.search.backend.protocol</li>
|
||||||
|
* <li>spring.jpa.properties.hibernate.search.backend.username</li>
|
||||||
|
* <li>spring.jpa.properties.hibernate.search.backend.password</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param registry the Spring dynamic property registry
|
||||||
|
* @param container the Elasticsearch container
|
||||||
|
*/
|
||||||
|
public static void registerElasticsearchProperties(DynamicPropertyRegistry registry, ElasticsearchContainer container) {
|
||||||
|
registry.add("spring.jpa.properties.hibernate.search.backend.hosts", container::getHttpHostAddress);
|
||||||
|
registry.add("spring.jpa.properties.hibernate.search.backend.protocol", () -> "http");
|
||||||
|
registry.add("spring.jpa.properties.hibernate.search.backend.username", () -> "");
|
||||||
|
registry.add("spring.jpa.properties.hibernate.search.backend.password", () -> "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers both PostgreSQL and Elasticsearch container properties with Spring's DynamicPropertyRegistry.
|
||||||
|
*
|
||||||
|
* @param registry the Spring dynamic property registry
|
||||||
|
* @param postgres the PostgreSQL container
|
||||||
|
* @param elasticsearch the Elasticsearch container
|
||||||
|
*/
|
||||||
|
public static void registerPostgresAndElasticsearchProperties(
|
||||||
|
DynamicPropertyRegistry registry,
|
||||||
|
PostgreSQLContainer<?> postgres,
|
||||||
|
ElasticsearchContainer elasticsearch
|
||||||
|
) {
|
||||||
|
registerPostgresProperties(registry, postgres);
|
||||||
|
registerElasticsearchProperties(registry, elasticsearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Elasticsearch HTTP URL for a container.
|
||||||
|
* <p>
|
||||||
|
* Example: "http://localhost:49152"
|
||||||
|
*
|
||||||
|
* @param container the Elasticsearch container
|
||||||
|
* @return the full HTTP URL to the Elasticsearch instance
|
||||||
|
*/
|
||||||
|
public static String getElasticsearchHttpUrl(ElasticsearchContainer container) {
|
||||||
|
return "http://" + container.getHost() + ":" + container.getMappedPort(9200);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
32
src/test/resources/test-elasticsearch-lastn.yaml
Normal file
32
src/test/resources/test-elasticsearch-lastn.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
hapi:
|
||||||
|
fhir:
|
||||||
|
fhir_version: r4
|
||||||
|
cr_enabled: false
|
||||||
|
lastn_enabled: true
|
||||||
|
advanced_lucene_indexing: true
|
||||||
|
store_resource_in_lucene_index_enabled: true
|
||||||
|
search_index_full_text_enabled: true
|
||||||
|
|
||||||
|
spring:
|
||||||
|
main:
|
||||||
|
allow-bean-definition-overriding: true
|
||||||
|
# H2 in-memory datasource for this test
|
||||||
|
datasource:
|
||||||
|
url: jdbc:h2:mem:dbr4
|
||||||
|
# Elasticsearch URI - set dynamically for ElasticConfigCondition to enable ElasticsearchBootSvcImpl
|
||||||
|
elasticsearch:
|
||||||
|
uris: # Set dynamically from ElasticsearchContainer via TestContainerHelper.getElasticsearchHttpUrl()
|
||||||
|
jpa:
|
||||||
|
properties:
|
||||||
|
hibernate:
|
||||||
|
search:
|
||||||
|
enabled: true
|
||||||
|
backend:
|
||||||
|
type: elasticsearch
|
||||||
|
analysis:
|
||||||
|
configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer
|
||||||
|
# Elasticsearch connection properties - set dynamically by TestContainerHelper.registerElasticsearchProperties()
|
||||||
|
hosts: # Set dynamically from ElasticsearchContainer
|
||||||
|
protocol: # Set dynamically to "http"
|
||||||
|
username: # Set dynamically to ""
|
||||||
|
password: # Set dynamically to ""
|
||||||
@@ -9,13 +9,25 @@ hapi:
|
|||||||
spring:
|
spring:
|
||||||
main:
|
main:
|
||||||
allow-bean-definition-overriding: true
|
allow-bean-definition-overriding: true
|
||||||
|
# PostgreSQL datasource properties - set dynamically by TestContainerHelper.registerPostgresProperties()
|
||||||
|
datasource:
|
||||||
|
url: # Set dynamically from PostgreSQLContainer
|
||||||
|
username: # Set dynamically from PostgreSQLContainer
|
||||||
|
password: # Set dynamically from PostgreSQLContainer
|
||||||
|
driver-class-name: # Set dynamically from PostgreSQLContainer
|
||||||
jpa:
|
jpa:
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
|
# PostgreSQL dialect - set dynamically by TestContainerHelper.registerPostgresProperties()
|
||||||
|
dialect: # Set dynamically to ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect
|
||||||
search:
|
search:
|
||||||
enabled: true
|
enabled: true
|
||||||
backend:
|
backend:
|
||||||
type: elasticsearch
|
type: elasticsearch
|
||||||
analysis:
|
analysis:
|
||||||
configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer
|
configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer
|
||||||
# Connection properties will be set dynamically by the test via @DynamicPropertySource
|
# Elasticsearch connection properties - set dynamically by TestContainerHelper.registerElasticsearchProperties()
|
||||||
|
hosts: # Set dynamically from ElasticsearchContainer
|
||||||
|
protocol: # Set dynamically to "http"
|
||||||
|
username: # Set dynamically to ""
|
||||||
|
password: # Set dynamically to ""
|
||||||
|
|||||||
@@ -9,9 +9,17 @@ hapi:
|
|||||||
spring:
|
spring:
|
||||||
main:
|
main:
|
||||||
allow-bean-definition-overriding: true
|
allow-bean-definition-overriding: true
|
||||||
|
# PostgreSQL datasource properties - set dynamically by TestContainerHelper.registerPostgresProperties()
|
||||||
|
datasource:
|
||||||
|
url: # Set dynamically from PostgreSQLContainer
|
||||||
|
username: # Set dynamically from PostgreSQLContainer
|
||||||
|
password: # Set dynamically from PostgreSQLContainer
|
||||||
|
driver-class-name: # Set dynamically from PostgreSQLContainer
|
||||||
jpa:
|
jpa:
|
||||||
properties:
|
properties:
|
||||||
hibernate:
|
hibernate:
|
||||||
|
# PostgreSQL dialect - set dynamically by TestContainerHelper.registerPostgresProperties()
|
||||||
|
dialect: # Set dynamically to ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect
|
||||||
search:
|
search:
|
||||||
enabled: true
|
enabled: true
|
||||||
backend:
|
backend:
|
||||||
|
|||||||
Reference in New Issue
Block a user