From bb3dff0294c9cf958ac12d0e75e625fd49296034 Mon Sep 17 00:00:00 2001 From: Jasper Kamerling Date: Thu, 7 Dec 2023 11:15:16 +0100 Subject: [PATCH] FDP-1811: Add monitoring test Signed-off-by: Jasper Kamerling --- application/build.gradle.kts | 1 + .../monitoring/MonitoringService.kt | 29 +++- .../monitoring/MonitoringServiceTest.kt | 134 ++++++++++++++++++ 3 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 application/src/test/kotlin/org/gxf/soapbridge/monitoring/MonitoringServiceTest.kt diff --git a/application/build.gradle.kts b/application/build.gradle.kts index 40a6b1b..dda87d9 100644 --- a/application/build.gradle.kts +++ b/application/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-engine") testImplementation("org.junit.jupiter:junit-jupiter-params") testImplementation("org.mockito:mockito-junit-jupiter") + testImplementation("org.assertj:assertj-core") // Generate test and integration test reports jacocoAggregation(project(":application")) diff --git a/application/src/main/kotlin/org/gxf/soapbridge/monitoring/MonitoringService.kt b/application/src/main/kotlin/org/gxf/soapbridge/monitoring/MonitoringService.kt index a196c79..6fa0d0f 100644 --- a/application/src/main/kotlin/org/gxf/soapbridge/monitoring/MonitoringService.kt +++ b/application/src/main/kotlin/org/gxf/soapbridge/monitoring/MonitoringService.kt @@ -14,21 +14,40 @@ class MonitoringService( companion object { private const val METRIC_PREFIX = "gxf.soap.bridge" + const val CACHE_SIZE_METRIC = "${METRIC_PREFIX}.cache.size" + const val CONNECTION_TIMER_METRIC = "${METRIC_PREFIX}.cache.size" + + const val CONNECTION_TIMER_CONTEXT_TAG = "context" + const val CONNECTION_TIMER_SUCCESSFUL_TAG = "successful" + } + /** + * Creates a gauge to monitor the size of a cache. + * + * @param cache The cache to monitor, represented as a Map. + * @return A Gauge object that measures the size of the cache. + */ fun monitorCacheSize(cache: Map<*, *>) = Gauge - .builder("${METRIC_PREFIX}.cache.size", cache) { it.size.toDouble() } + .builder(CACHE_SIZE_METRIC, cache) { it.size.toDouble() } .register(registry) + /** + * Records the connection time for a request. + * + * @param startTime The start time of the request. + * @param context The context of the request. + * @param successful Flag indicating if the request was successful. + */ fun recordConnectionTime(startTime: Instant, context: String, successful: Boolean) { val duration = Duration.between(startTime, Instant.now()) Timer - .builder("${METRIC_PREFIX}.connection.timer") - .description("Counts the successful requests to the Maki API") - .tag("context", context) - .tag("successful", successful.toString()) + .builder(CONNECTION_TIMER_METRIC) + .description("Counts the request time of a soap call") + .tag(CONNECTION_TIMER_CONTEXT_TAG, context) + .tag(CONNECTION_TIMER_SUCCESSFUL_TAG, successful.toString()) .register(registry) .record(duration) } diff --git a/application/src/test/kotlin/org/gxf/soapbridge/monitoring/MonitoringServiceTest.kt b/application/src/test/kotlin/org/gxf/soapbridge/monitoring/MonitoringServiceTest.kt new file mode 100644 index 0000000..fa30e4c --- /dev/null +++ b/application/src/test/kotlin/org/gxf/soapbridge/monitoring/MonitoringServiceTest.kt @@ -0,0 +1,134 @@ +package org.gxf.soapbridge.monitoring + +import io.micrometer.core.instrument.ImmutableTag +import io.micrometer.core.instrument.MeterRegistry +import io.micrometer.core.instrument.search.Search +import io.micrometer.core.instrument.simple.SimpleMeterRegistry +import org.assertj.core.api.Assertions.assertThat +import org.gxf.soapbridge.monitoring.MonitoringService.Companion.CACHE_SIZE_METRIC +import org.gxf.soapbridge.monitoring.MonitoringService.Companion.CONNECTION_TIMER_CONTEXT_TAG +import org.gxf.soapbridge.monitoring.MonitoringService.Companion.CONNECTION_TIMER_METRIC +import org.gxf.soapbridge.monitoring.MonitoringService.Companion.CONNECTION_TIMER_SUCCESSFUL_TAG +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import java.time.Instant +import java.util.concurrent.TimeUnit + + +class MonitoringServiceTest { + + private lateinit var meterRegistry: MeterRegistry + private lateinit var monitoringService: MonitoringService + + @BeforeEach + fun setUp() { + meterRegistry = SimpleMeterRegistry() + monitoringService = MonitoringService(meterRegistry) + } + + @AfterEach + fun tearDown() { + meterRegistry.clear() + } + + @Test + fun `cache size monitor matches map size`() { + // Meter should not exist before creating it + assertNull(meterRegistry.find(CACHE_SIZE_METRIC).gauge()) + + // Create cache map and gauge + val cacheMap = mutableMapOf() + val gauge = monitoringService.monitorCacheSize(cacheMap) + + // Check if the meter exists and is 0 + assertNotNull(meterRegistry.find(CACHE_SIZE_METRIC).gauge()) + assertEquals(0.0, gauge.value()) + + // After adding an entry it should be 1 + cacheMap["key"] = "value" + assertEquals(1.0, gauge.value()) + + // After reassigning the key it should stay at 1 + cacheMap["key"] = "new-value" + assertEquals(1.0, gauge.value()) + + // After adding a second key it should be 2 + cacheMap["new-key"] = "new-value" + assertEquals(2.0, gauge.value()) + } + + @Test + fun `connection timer creates multiple timers`() { + val startTime = Instant.now() + val contextOne = "test-context-one" + val successfulOne = true + + val expectedTagsOne = listOf( + ImmutableTag(CONNECTION_TIMER_CONTEXT_TAG, contextOne), + ImmutableTag(CONNECTION_TIMER_SUCCESSFUL_TAG, successfulOne.toString()) + ) + + val contextTwo = "test-context-two" + val successfulTwo = false + val expectedTagsTwo = listOf( + ImmutableTag(CONNECTION_TIMER_CONTEXT_TAG, contextTwo), + ImmutableTag(CONNECTION_TIMER_SUCCESSFUL_TAG, successfulTwo.toString()) + ) + monitoringService.recordConnectionTime(startTime, contextOne, successfulOne) + monitoringService.recordConnectionTime(startTime, contextTwo, successfulTwo) + + val timerOne = + Search.`in`(meterRegistry) + .name(CONNECTION_TIMER_METRIC) + .tags(expectedTagsOne) + .timer() + + assertNotNull(timerOne) + + val timerTwo = + Search.`in`(meterRegistry) + .name(CONNECTION_TIMER_METRIC) + .tags(expectedTagsTwo) + .timer() + assertNotNull(timerTwo) + + } + + @Test + fun `connection timer records request times`() { + val startTime = Instant.now() + val context = "test-context" + val successful = true + + monitoringService.recordConnectionTime(startTime, context, successful) + + // Find the timer by name and tags + val expectedTags = listOf( + ImmutableTag(CONNECTION_TIMER_CONTEXT_TAG, context), + ImmutableTag(CONNECTION_TIMER_SUCCESSFUL_TAG, successful.toString()) + ) + val timer = Search.`in`(meterRegistry) + .name(CONNECTION_TIMER_METRIC) + .tags(expectedTags) + .timer() + assertNotNull(timer) + check(timer != null) + + + assertThat(timer.count()).isEqualTo(1) + assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isNotEqualTo(0) + assertThat(timer.max(TimeUnit.NANOSECONDS)).isNotEqualTo(0) + assertThat(timer.mean(TimeUnit.NANOSECONDS)).isNotEqualTo(0) + + monitoringService.recordConnectionTime(startTime, context, successful) + + assertThat(timer.count()).isEqualTo(2) + assertThat(timer.totalTime(TimeUnit.NANOSECONDS)).isNotEqualTo(0) + assertThat(timer.max(TimeUnit.NANOSECONDS)).isNotEqualTo(0) + assertThat(timer.mean(TimeUnit.NANOSECONDS)).isNotEqualTo(0) + + } + +} \ No newline at end of file