From e59b669860c2b1404461e8b6c12da0b58810b67a Mon Sep 17 00:00:00 2001 From: Thomas BOUSSELIN Date: Thu, 12 Dec 2024 17:09:26 +0100 Subject: [PATCH] feat: test for ContextSourceUtils and ContextSourceCaller --- search-service/config/detekt/baseline.xml | 1 - .../search/csr/service/ContextSourceCaller.kt | 2 + .../search/csr/service/ContextSourceUtils.kt | 9 +-- .../csr/service/ContextSourceCallerTests.kt | 52 ++++++++++++++ .../csr/service/ContextSourceUtilsTests.kt | 70 +++++++++++++++++++ 5 files changed, 129 insertions(+), 5 deletions(-) diff --git a/search-service/config/detekt/baseline.xml b/search-service/config/detekt/baseline.xml index 8dbac235f..7f380d5b4 100644 --- a/search-service/config/detekt/baseline.xml +++ b/search-service/config/detekt/baseline.xml @@ -32,7 +32,6 @@ LongParameterList:TemporalEntityHandler.kt$TemporalEntityHandler$( @RequestHeader httpHeaders: HttpHeaders, @PathVariable entityId: URI, @PathVariable attrId: String, @PathVariable instanceId: URI, @RequestBody requestBody: Mono<String>, @AllowedParameters(notImplemented = [QP.LOCAL, QP.VIA]) @RequestParam queryParams: MultiValueMap<String, String> ) LongParameterList:V0_29__JsonLd_migration.kt$V0_29__JsonLd_migration$( entityId: URI, attributeName: ExpandedTerm, datasetId: URI?, attributePayload: ExpandedAttributeInstance, ngsiLdAttributeInstance: NgsiLdAttributeInstance, defaultCreatedAt: ZonedDateTime ) NestedBlockDepth:V0_29__JsonLd_migration.kt$V0_29__JsonLd_migration$override fun migrate(context: Context) - SpacingBetweenPackageAndImports:ContextSourceCaller.kt$ SwallowedException:TemporalQueryUtils.kt$e: IllegalArgumentException diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt b/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt index fc48408a9..c97d17ee4 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceCaller.kt @@ -26,7 +26,9 @@ import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.awaitBodyOrNull import org.springframework.web.reactive.function.client.awaitExchange import java.net.URI + typealias QueryEntityResponse = Pair, Int?> + object ContextSourceCaller { val logger: Logger = LoggerFactory.getLogger(javaClass) diff --git a/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtils.kt b/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtils.kt index f35257dd6..54979973e 100644 --- a/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtils.kt +++ b/search-service/src/main/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtils.kt @@ -35,14 +35,15 @@ object ContextSourceUtils { localEntities: List, remoteEntitiesWithCSR: List ): IorNel> { - val mergedEntityMap = localEntities.associateBy { it[JSONLD_ID_TERM] }.toMutableMap() + val mergedEntityMap = localEntities.map { it.toMutableMap() }.associateBy { it[JSONLD_ID_TERM] }.toMutableMap() - val warnings = remoteEntitiesWithCSR.mapNotNull { (entities, csr) -> + val warnings = remoteEntitiesWithCSR.sortedBy { (_, csr) -> csr.isAuxiliary() }.mapNotNull { (entities, csr) -> val test = either { entities.forEach { entity -> val id = entity[JSONLD_ID_TERM] - mergedEntityMap[id] = mergedEntityMap[id] - ?.let { getMergeNewValues(it, entity, csr).bind() } ?: entity + mergedEntityMap[id] + ?.let { it.putAll(getMergeNewValues(it, entity, csr).bind()) } + ?: run { mergedEntityMap[id] = entity.toMutableMap() } } null } diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt index 086ef0fd6..66dc453a9 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceCallerTests.kt @@ -9,6 +9,7 @@ import com.egm.stellio.shared.util.APIC_COMPOUND_CONTEXT import com.egm.stellio.shared.util.GEO_JSON_CONTENT_TYPE import com.egm.stellio.shared.util.GEO_JSON_MEDIA_TYPE import com.egm.stellio.shared.util.JsonUtils.serializeObject +import com.egm.stellio.shared.util.RESULTS_COUNT_HEADER import com.egm.stellio.shared.util.assertJsonPayloadsAreEqual import com.egm.stellio.shared.util.toUri import com.github.tomakehurst.wiremock.client.WireMock.get @@ -23,7 +24,9 @@ import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo import com.github.tomakehurst.wiremock.client.WireMock.verify import com.github.tomakehurst.wiremock.junit5.WireMockTest import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertInstanceOf +import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -71,6 +74,55 @@ class ContextSourceCallerTests { } """.trimIndent() + @Test + fun `queryContextSourceEntities should return the count and the entities when the request succeed`() = runTest { + val csr = gimmeRawCSR() + val path = "/ngsi-ld/v1/entities" + val count = 222 + val payload = "[$entityWithSysAttrs, $entityWithSysAttrs]" + stubFor( + get(urlMatching(path)) + .willReturn( + ok() + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE) + .withHeader(RESULTS_COUNT_HEADER, count.toString()).withBody(payload) + ) + ) + + val response = ContextSourceCaller.queryContextSourceEntities( + HttpHeaders.EMPTY, + csr, + true, + emptyParams + ).getOrNull() + assertNotNull(response) + assertEquals(count, response!!.second) + assertJsonPayloadsAreEqual(entityWithSysAttrs, serializeObject(response.first.first())) + } + + @Test + fun `queryContextSourceEntities should return a RevalidationFailedWarning when receiving bad payload`() = runTest { + val csr = gimmeRawCSR() + val path = "/ngsi-ld/v1/entities" + stubFor( + get(urlMatching(path)) + .willReturn( + ok() + .withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE).withBody(entityWithSysAttrs) + ) + ) + + val response = ContextSourceCaller.queryContextSourceEntities( + HttpHeaders.EMPTY, + csr, + false, + emptyParams + ) + + assertTrue(response.isLeft()) + assertInstanceOf(RevalidationFailedWarning::class.java, response.leftOrNull()) + } + @Test fun `retrieveContextSourceEntity should return the entity when the request succeed`() = runTest { val csr = gimmeRawCSR() diff --git a/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtilsTests.kt b/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtilsTests.kt index f2ff972b1..c78ba6771 100644 --- a/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtilsTests.kt +++ b/search-service/src/test/kotlin/com/egm/stellio/search/csr/service/ContextSourceUtilsTests.kt @@ -7,6 +7,7 @@ import com.egm.stellio.search.csr.model.MiscellaneousWarning import com.egm.stellio.search.csr.model.Mode import com.egm.stellio.shared.model.CompactedAttributeInstance import com.egm.stellio.shared.model.CompactedEntity +import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_ID_TERM import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_TYPE_TERM import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_VALUE_TERM import com.egm.stellio.shared.util.JsonLdUtils.NGSILD_CREATED_AT_TERM @@ -230,4 +231,73 @@ class ContextSourceUtilsTests { assertEquals(entityWithNameAndSurname, entity) } } + + @Test + fun `merge entitiesList should merge entities with the same id`() = runTest { + val mergedEntity = ContextSourceUtils.mergeEntitiesLists( + listOf(entityWithName), + listOf(listOf(entityWithLastName) to auxiliaryCSR, listOf(entityWithSurName) to inclusiveCSR) + ).getOrNull() + assertEquals(1, mergedEntity!!.size) + assertEquals(entityWithName + entityWithLastName + entityWithSurName, mergedEntity.first()) + } + + @Test + fun `merge entitiesList should not keep conflicting data from an auxiliary csr`() = runTest { + val mergedEntity = ContextSourceUtils.mergeEntitiesLists( + listOf(moreRecentEntity), + listOf(listOf(evenMoreRecentEntity) to auxiliaryCSR, listOf(entityWithName) to inclusiveCSR) + ).getOrNull() + assertEquals(1, mergedEntity!!.size) + assertEquals(moreRecentEntity, mergedEntity.first()) + } + + @Test + fun `merge entitiesList should add entities with the different ids`() = runTest { + val entityWithDifferentId = minimalEntity.toMutableMap() + (JSONLD_ID_TERM to "differentId") + val entityWithAnotherDifferentId = minimalEntity.toMutableMap() + (JSONLD_ID_TERM to "anotherDifferentId") + val mergedEntity = ContextSourceUtils.mergeEntitiesLists( + listOf(entityWithName), + listOf(listOf(entityWithDifferentId) to inclusiveCSR, listOf(entityWithAnotherDifferentId) to inclusiveCSR) + ).getOrNull() + assertThat(mergedEntity) + .hasSize(3) + .contains(entityWithName, entityWithDifferentId, entityWithAnotherDifferentId) + } + + @Test + fun `merge entitiesList should merge using getMergeNewValues and return the received warnings`() = runTest { + val warning1 = MiscellaneousWarning("1", inclusiveCSR) + val warning2 = MiscellaneousWarning("2", inclusiveCSR) + mockkObject(ContextSourceUtils) { + every { ContextSourceUtils.getMergeNewValues(any(), any(), any()) } returns + warning1.left() andThen warning2.left() + + val (warnings, entity) = ContextSourceUtils.mergeEntitiesLists( + listOf(entityWithName), + listOf(listOf(entityWithName) to inclusiveCSR, listOf(entityWithName) to inclusiveCSR) + ).toPair() + verify(exactly = 2) { ContextSourceUtils.getMergeNewValues(any(), any(), any()) } + assertThat(warnings).hasSize(2).contains(warning1, warning2) + assertEquals(listOf(entityWithName), entity) + } + } + + @Test + fun `merge entitiesList should not merge List in error`() = runTest { + mockkObject(ContextSourceUtils) { + val (warnings, entity) = ContextSourceUtils.mergeEntitiesLists( + listOf(entityWithName), + listOf( + listOf(invalidEntityWithLastName) to inclusiveCSR, + listOf(entityWithSurName) to inclusiveCSR + ) + ).toPair() + + verify(exactly = 2) { ContextSourceUtils.getMergeNewValues(any(), any(), any()) } + assertThat(warnings).hasSize(1) + val entityWithNameAndSurname = entityWithName.plus("surName" to nameAttribute) + assertEquals(listOf(entityWithNameAndSurname), entity) + } + } }