From c420e6c17672977b117898246fcb1e018517406c Mon Sep 17 00:00:00 2001 From: Michael Folz Date: Mon, 19 Aug 2024 14:51:57 +0200 Subject: [PATCH] #323 - Extended referencedCriteria and Valueset Search - add integration test for CodeableConceptService --- .../es/CodeableConceptService.java | 2 +- .../es/CodeableConceptServiceIT.java | 129 ++++++++++++++++++ .../es/TerminologyEsServiceIT.java | 2 +- .../terminology/es/cc_testdata.json | 6 + .../terminology/es/codeable_concept.json | 62 +++++++++ .../{testData.json => ontology_testdata.json} | 0 6 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 src/test/java/de/numcodex/feasibility_gui_backend/terminology/es/CodeableConceptServiceIT.java create mode 100644 src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/cc_testdata.json create mode 100644 src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/codeable_concept.json rename src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/{testData.json => ontology_testdata.json} (100%) diff --git a/src/main/java/de/numcodex/feasibility_gui_backend/terminology/es/CodeableConceptService.java b/src/main/java/de/numcodex/feasibility_gui_backend/terminology/es/CodeableConceptService.java index d61db980..e760094e 100644 --- a/src/main/java/de/numcodex/feasibility_gui_backend/terminology/es/CodeableConceptService.java +++ b/src/main/java/de/numcodex/feasibility_gui_backend/terminology/es/CodeableConceptService.java @@ -40,7 +40,7 @@ public CcSearchResult performCodeableConceptSearchWithRepoAndPaging(String keywo if (valueSets != null && !valueSets.isEmpty()) { searchHitPage = repo .findByNameOrTermcodeMultiMatch1Filter(keyword, - "valuesets", + "value_sets", valueSets, PageRequest.of(page, pageSize)); } else { diff --git a/src/test/java/de/numcodex/feasibility_gui_backend/terminology/es/CodeableConceptServiceIT.java b/src/test/java/de/numcodex/feasibility_gui_backend/terminology/es/CodeableConceptServiceIT.java new file mode 100644 index 00000000..eecce3d0 --- /dev/null +++ b/src/test/java/de/numcodex/feasibility_gui_backend/terminology/es/CodeableConceptServiceIT.java @@ -0,0 +1,129 @@ +package de.numcodex.feasibility_gui_backend.terminology.es; + +import de.numcodex.feasibility_gui_backend.terminology.es.repository.CodeableConceptEsRepository; +import de.numcodex.feasibility_gui_backend.terminology.es.repository.OntologyItemNotFoundException; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.elasticsearch.DataElasticsearchTest; +import org.springframework.context.annotation.Import; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.reactive.function.client.WebClient; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.images.PullPolicy; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.io.IOException; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Tag("terminology") +@Tag("elasticsearch") +@Import({CodeableConceptService.class}) +@Testcontainers +@DataElasticsearchTest( properties = { + "app.elastic.filter=context,terminology" +}) +public class CodeableConceptServiceIT { + + @Autowired + private ElasticsearchOperations operations; + + @Autowired + private CodeableConceptEsRepository repo; + + @Autowired + private CodeableConceptService codeableConceptService; + + @Container + public static ElasticsearchContainer elastic = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.15.0") + .withEnv("discovery.type", "single-node") + .withEnv("xpack.security.enabled", "false") + .withExposedPorts(9200) + .withStartupAttempts(3) + .withImagePullPolicy(PullPolicy.alwaysPull()) + .waitingFor(Wait.forHttp("/health").forStatusCodeMatching(c -> c >= 200 && c <= 500)); + + @DynamicPropertySource + static void esProperties(DynamicPropertyRegistry registry) { + registry.add("spring.elasticsearch.uris", elastic::getHttpHostAddress); + } + + @BeforeAll + static void setUp() throws IOException { + elastic.start(); + System.out.println(elastic.getHttpHostAddress()); + WebClient webClient = WebClient.builder().baseUrl("http://" + elastic.getHttpHostAddress()).build(); + webClient.put() + .uri("/codeable_concept") + .body(BodyInserters.fromResource(new ClassPathResource("codeable_concept.json", CodeableConceptServiceIT.class))) + .retrieve() + .toBodilessEntity() + .block(); + + webClient.post() + .uri("/codeable_concept/_bulk") + .body(BodyInserters.fromResource(new ClassPathResource("cc_testdata.json", CodeableConceptServiceIT.class))) + .retrieve() + .toBodilessEntity() + .block(); + } + + @AfterAll + static void tearDown() { + elastic.stop(); + } + + @Test + void testPerformCodeableConceptSearchWithRepoAndPaging_findsOne() { + var page = assertDoesNotThrow (() -> codeableConceptService.performCodeableConceptSearchWithRepoAndPaging("foo", List.of(), 20, 0)); + + assertNotNull(page); + assertThat(page.getTotalHits()).isOne(); + Assertions.assertEquals("A1.0", page.getResults().get(0).code()); + } + + @Test + void testPerformCodeableConceptSearchWithRepoAndPaging_findsNone() { + var page = assertDoesNotThrow (() -> codeableConceptService.performCodeableConceptSearchWithRepoAndPaging("something-not-found", List.of(), 20, 0)); + + assertNotNull(page); + assertThat(page.getTotalHits()).isZero(); + } + + @Test + void testPerformCodeableConceptSearchWithRepoAndPaging_findsTwoOrOneDependingOnFilter() { + var pageNoFilter = assertDoesNotThrow (() -> codeableConceptService.performCodeableConceptSearchWithRepoAndPaging("ba", List.of(), 20, 0)); + var pageOneFilter = assertDoesNotThrow (() -> codeableConceptService.performCodeableConceptSearchWithRepoAndPaging("ba", List.of("some-value-set"), 20, 0)); + + assertNotNull(pageNoFilter); + assertNotNull(pageOneFilter); + Assertions.assertEquals(2, pageNoFilter.getTotalHits()); + Assertions.assertEquals(1, pageOneFilter.getTotalHits()); + } + + @Test + void testGetSearchResultEntryByCode_succeeds() { + var result = assertDoesNotThrow(() -> codeableConceptService.getSearchResultEntryByCode("A1.1")); + + assertNotNull(result); + Assertions.assertEquals("bar", result.display()); + Assertions.assertEquals("A1.1", result.code()); + Assertions.assertEquals("2012", result.version()); + Assertions.assertEquals("another-system", result.system()); + } + + @Test + void testGetSearchResultEntryByCode_throwsOnNotFound() { + assertThrows(OntologyItemNotFoundException.class, () -> codeableConceptService.getSearchResultEntryByCode("something-not-found")); + } +} diff --git a/src/test/java/de/numcodex/feasibility_gui_backend/terminology/es/TerminologyEsServiceIT.java b/src/test/java/de/numcodex/feasibility_gui_backend/terminology/es/TerminologyEsServiceIT.java index f1a96b18..d5c62307 100644 --- a/src/test/java/de/numcodex/feasibility_gui_backend/terminology/es/TerminologyEsServiceIT.java +++ b/src/test/java/de/numcodex/feasibility_gui_backend/terminology/es/TerminologyEsServiceIT.java @@ -78,7 +78,7 @@ static void setUp() throws IOException { webClient.post() .uri("/ontology/_bulk") - .body(BodyInserters.fromResource(new ClassPathResource("testData.json", TerminologyEsServiceIT.class))) + .body(BodyInserters.fromResource(new ClassPathResource("ontology_testdata.json", TerminologyEsServiceIT.class))) .retrieve() .toBodilessEntity() .block(); diff --git a/src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/cc_testdata.json b/src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/cc_testdata.json new file mode 100644 index 00000000..6d048900 --- /dev/null +++ b/src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/cc_testdata.json @@ -0,0 +1,6 @@ +{"index": {"_index": "codeable_concept", "_id": "A1.0"}} +{"termcode": {"code": "A1.0", "display": "foo", "system": "some-system", "version": 2010}, "value_sets": []} +{"index": {"_index": "codeable_concept", "_id": "A1.1"}} +{"termcode": {"code": "A1.1", "display": "bar", "system": "another-system", "version": 2012}, "value_sets": ["some-value-set"]} +{"index": {"_index": "codeable_concept", "_id": "A2.0"}} +{"termcode": {"code": "A2.0", "display": "baz", "system": "some-system", "version": 2023}, "value_sets": []} diff --git a/src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/codeable_concept.json b/src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/codeable_concept.json new file mode 100644 index 00000000..2a9ac497 --- /dev/null +++ b/src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/codeable_concept.json @@ -0,0 +1,62 @@ +{ + "settings": { + "analysis": { + "tokenizer": { + "edge_ngram_tokenizer": { + "type": "edge_ngram", + "min_gram": 1, + "max_gram": 20, + "token_chars": [ + "letter", + "digit" + ] + } + }, + "analyzer": { + "edge_ngram_analyzer": { + "type": "custom", + "tokenizer": "edge_ngram_tokenizer", + "filter": [ + "lowercase" + ] + }, + "lowercase_analyzer": { + "type": "custom", + "tokenizer": "standard", + "filter": [ + "lowercase" + ] + } + } + } + }, + "mappings": { + "properties": { + "termcode": { + "properties": { + "code": { + "type": "text", + "analyzer": "edge_ngram_analyzer", + "search_analyzer": "lowercase_analyzer" + }, + "display": { + "type": "text", + "analyzer": "edge_ngram_analyzer", + "search_analyzer": "lowercase_analyzer" + }, + "system": { + "type": "text", + "index": false + }, + "version": { + "type": "long", + "index": false + } + } + }, + "value_sets": { + "type": "keyword" + } + } + } +} \ No newline at end of file diff --git a/src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/testData.json b/src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/ontology_testdata.json similarity index 100% rename from src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/testData.json rename to src/test/resources/de/numcodex/feasibility_gui_backend/terminology/es/ontology_testdata.json