diff --git a/build.gradle.kts b/build.gradle.kts index 9ecea10..433c2f5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Exactpro (Exactpro Systems Limited) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import org.jlleitschuh.gradle.ktlint.reporter.ReporterType plugins { @@ -79,7 +95,7 @@ kotlin { } ktlint { - version.set("0.50.0") + version.set("1.6.0") reporters { reporter(ReporterType.HTML) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 81d7b06..11c32b4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,5 +21,5 @@ testcontainers-bom = { group = "org.testcontainers", name = "testcontainers-bom" [plugins] th2-publish = { id = "com.exactpro.th2.gradle.publish", version = "0.3.5" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "12.3.0" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "13.0.0" } compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version = "0.18.1" } \ No newline at end of file diff --git a/src/main/kotlin/com/exactpro/th2/test/extension/CleanupExtension.kt b/src/main/kotlin/com/exactpro/th2/test/extension/CleanupExtension.kt index cd4924a..3ad419f 100644 --- a/src/main/kotlin/com/exactpro/th2/test/extension/CleanupExtension.kt +++ b/src/main/kotlin/com/exactpro/th2/test/extension/CleanupExtension.kt @@ -27,7 +27,11 @@ import org.junit.jupiter.api.extension.ParameterResolver import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils import java.util.LinkedList -public class CleanupExtension : BeforeEachCallback, BeforeAllCallback, AfterEachCallback, ParameterResolver { +public class CleanupExtension : + BeforeEachCallback, + BeforeAllCallback, + AfterEachCallback, + ParameterResolver { override fun beforeEach(context: ExtensionContext) { context.getStore(NAMESPACE).put(AFTER_TEST_KEY, ClosableRegistry(Registry())) } @@ -48,7 +52,11 @@ public class CleanupExtension : BeforeEachCallback, BeforeAllCallback, AfterEach public fun add(resource: AutoCloseable) { add(RandomStringUtils.randomAlphabetic(10), resource) } - public fun add(name: String, resource: AutoCloseable) { + + public fun add( + name: String, + resource: AutoCloseable, + ) { check(resources.find { it.first == name } == null) { "duplicated resource $name" } @@ -59,7 +67,6 @@ public class CleanupExtension : BeforeEachCallback, BeforeAllCallback, AfterEach private class ClosableRegistry( val registry: Registry, ) : ExtensionContext.Store.CloseableResource { - override fun close() { registry.resources.descendingIterator().forEach { (name, resource) -> runCatching { @@ -72,18 +79,22 @@ public class CleanupExtension : BeforeEachCallback, BeforeAllCallback, AfterEach } } - override fun supportsParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Boolean { - return parameterContext.parameter.type == Registry::class.java - } + override fun supportsParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext, + ): Boolean = parameterContext.parameter.type == Registry::class.java - override fun resolveParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Any { + override fun resolveParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext, + ): Any { val store = extensionContext.getStore(NAMESPACE) // if we have registry for AFTER_TEST_KEY key it means the parameter is resolved for test method // or in before val autoClosableRegistry = ( store.get(AFTER_TEST_KEY, ClosableRegistry::class.java) ?: store.get(AFTER_ALL_KEY, ClosableRegistry::class.java) - ) + ) return autoClosableRegistry?.registry ?: error("registry is not created") } diff --git a/src/main/kotlin/com/exactpro/th2/test/extension/ReflectionExtensions.kt b/src/main/kotlin/com/exactpro/th2/test/extension/ReflectionExtensions.kt index 09e2375..2840c23 100644 --- a/src/main/kotlin/com/exactpro/th2/test/extension/ReflectionExtensions.kt +++ b/src/main/kotlin/com/exactpro/th2/test/extension/ReflectionExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,11 +21,10 @@ import org.junit.platform.commons.support.ReflectionSupport import java.lang.reflect.Field import kotlin.reflect.KClass -private fun List.definitions(): String { - return joinToString { +private fun List.definitions(): String = + joinToString { "${it.name} in ${it.declaringClass}" } -} internal inline fun Any.getSingle(fields: List): T { require(fields.size == 1) { @@ -53,10 +52,9 @@ internal inline fun Any.getFieldOrDefault(default: () -> T): T { ?: default() } -internal inline fun KClass<*>.findFields(): List { - return ReflectionSupport.findFields( +internal inline fun KClass<*>.findFields(): List = + ReflectionSupport.findFields( java, { field -> field.type == T::class.java }, HierarchyTraversalMode.TOP_DOWN, ) -} diff --git a/src/main/kotlin/com/exactpro/th2/test/extension/StoreExtensions.kt b/src/main/kotlin/com/exactpro/th2/test/extension/StoreExtensions.kt index fb92f95..ac1b513 100644 --- a/src/main/kotlin/com/exactpro/th2/test/extension/StoreExtensions.kt +++ b/src/main/kotlin/com/exactpro/th2/test/extension/StoreExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,7 @@ package com.exactpro.th2.test.extension import org.junit.jupiter.api.extension.ExtensionContext.Store -internal inline fun Store.getRequired(key: Any): T { - return checkNotNull(get(key, T::class.java)) { +internal inline fun Store.getRequired(key: Any): T = + checkNotNull(get(key, T::class.java)) { "no value of type ${T::class} by key $key" } -} diff --git a/src/main/kotlin/com/exactpro/th2/test/extension/Th2.kt b/src/main/kotlin/com/exactpro/th2/test/extension/Th2.kt index 979a704..acab562 100644 --- a/src/main/kotlin/com/exactpro/th2/test/extension/Th2.kt +++ b/src/main/kotlin/com/exactpro/th2/test/extension/Th2.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,9 +24,7 @@ internal object Th2 { @JvmField val NAMESPACE: Namespace = Namespace.create(Th2::class) - fun getAppConfigFolder(context: ExtensionContext): Path = - context.getStore(NAMESPACE).getRequired(Th2ConfigExtension.APP_CONFIG) + fun getAppConfigFolder(context: ExtensionContext): Path = context.getStore(NAMESPACE).getRequired(Th2ConfigExtension.APP_CONFIG) - fun getTestConfigFolder(context: ExtensionContext): Path = - context.getStore(NAMESPACE).getRequired(Th2ConfigExtension.TEST_CONFIG) + fun getTestConfigFolder(context: ExtensionContext): Path = context.getStore(NAMESPACE).getRequired(Th2ConfigExtension.TEST_CONFIG) } diff --git a/src/main/kotlin/com/exactpro/th2/test/extension/Th2CommonFactoryExtension.kt b/src/main/kotlin/com/exactpro/th2/test/extension/Th2CommonFactoryExtension.kt index e4e4e8e..58d009f 100644 --- a/src/main/kotlin/com/exactpro/th2/test/extension/Th2CommonFactoryExtension.kt +++ b/src/main/kotlin/com/exactpro/th2/test/extension/Th2CommonFactoryExtension.kt @@ -32,14 +32,19 @@ import org.junit.jupiter.api.extension.ParameterResolver import java.nio.file.Path import kotlin.io.path.outputStream -public class Th2CommonFactoryExtension : BeforeAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver { +public class Th2CommonFactoryExtension : + BeforeAllCallback, + BeforeEachCallback, + AfterEachCallback, + ParameterResolver { private class FactoryHolder( folder: Path, ) { - val factory: CommonFactory = CommonFactory.createFromArguments( - "-c", - folder.toAbsolutePath().toString(), - ) + val factory: CommonFactory = + CommonFactory.createFromArguments( + "-c", + folder.toAbsolutePath().toString(), + ) } private lateinit var appCommonFactory: FactoryHolder @@ -72,7 +77,10 @@ public class Th2CommonFactoryExtension : BeforeAllCallback, BeforeEachCallback, } } - private fun cleanUp(holder: FactoryHolder, name: String) { + private fun cleanUp( + holder: FactoryHolder, + name: String, + ) { LOGGER.info { "Cleaning factory $name" } runCatching { holder.factory.close() @@ -82,15 +90,16 @@ public class Th2CommonFactoryExtension : BeforeAllCallback, BeforeEachCallback, LOGGER.info { "Cleaning factory $name done" } } - override fun supportsParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Boolean { - return parameterContext.parameter.type == CommonFactory::class.java - } + override fun supportsParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext, + ): Boolean = parameterContext.parameter.type == CommonFactory::class.java override fun resolveParameter( parameterContext: ParameterContext, extensionContext: ExtensionContext, - ): CommonFactory { - return when { + ): CommonFactory = + when { parameterContext.isAnnotated(Th2AppFactory::class.java) -> { check(::appCommonFactory.isInitialized) { "app factory is not initialized" } appCommonFactory.factory @@ -103,7 +112,6 @@ public class Th2CommonFactoryExtension : BeforeAllCallback, BeforeEachCallback, else -> error("parameter must be annotated with ${Th2AppFactory::class} or ${Th2TestFactory::class} annotation") } - } private fun Path.writeConfiguration(name: String) { resolve(ConfigurationWriter.PROMETHEUS_CONFIG).outputStream().use { diff --git a/src/main/kotlin/com/exactpro/th2/test/extension/Th2ConfigExtension.kt b/src/main/kotlin/com/exactpro/th2/test/extension/Th2ConfigExtension.kt index dc7ac19..25ee639 100644 --- a/src/main/kotlin/com/exactpro/th2/test/extension/Th2ConfigExtension.kt +++ b/src/main/kotlin/com/exactpro/th2/test/extension/Th2ConfigExtension.kt @@ -26,7 +26,9 @@ import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.createDirectory import kotlin.io.path.deleteRecursively -public class Th2ConfigExtension : BeforeAllCallback, AfterAllCallback { +public class Th2ConfigExtension : + BeforeAllCallback, + AfterAllCallback { override fun beforeAll(context: ExtensionContext) { val store = context.getStore(Th2.NAMESPACE) val root = Files.createTempDirectory("th2") @@ -38,7 +40,9 @@ public class Th2ConfigExtension : BeforeAllCallback, AfterAllCallback { @OptIn(ExperimentalPathApi::class) override fun afterAll(context: ExtensionContext) { - context.getStore(Th2.NAMESPACE).get(ROOT_CONFIG, Path::class.java) + context + .getStore(Th2.NAMESPACE) + .get(ROOT_CONFIG, Path::class.java) ?.also { LOGGER.info { "Cleaning config dirs $it" } it.deleteRecursively() diff --git a/src/main/kotlin/com/exactpro/th2/test/extension/Th2CradleExtension.kt b/src/main/kotlin/com/exactpro/th2/test/extension/Th2CradleExtension.kt index e7007a3..ba0462f 100644 --- a/src/main/kotlin/com/exactpro/th2/test/extension/Th2CradleExtension.kt +++ b/src/main/kotlin/com/exactpro/th2/test/extension/Th2CradleExtension.kt @@ -54,7 +54,10 @@ public class Th2CradleExtension : private var cradleSpec: CradleSpec? = null private lateinit var cradle: CradleIntegration - override fun postProcessTestInstance(testInstance: Any, context: ExtensionContext) { + override fun postProcessTestInstance( + testInstance: Any, + context: ExtensionContext, + ) { val fields = testInstance::class.findFields().ifEmpty { return } cradleSpec = testInstance.getSingle(fields) } @@ -109,17 +112,24 @@ public class Th2CradleExtension : } } - override fun supportsParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Boolean { - return parameterContext.parameter.type == CradleManager::class.java - } + override fun supportsParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext, + ): Boolean = parameterContext.parameter.type == CradleManager::class.java - override fun resolveParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): Any { + override fun resolveParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext, + ): Any { val spec = checkNotNull(cradleSpec) { "cannot create Cradle manager without spec specified" } - val resource = extensionContext.getStore(Th2.NAMESPACE).getOrComputeIfAbsent( - Th2CradleExtension::class, - ) { - ClosableCradleResource(createManager(spec)) - }.let(ClosableCradleResource::class::cast) + val resource = + extensionContext + .getStore(Th2.NAMESPACE) + .getOrComputeIfAbsent( + Th2CradleExtension::class, + ) { + ClosableCradleResource(createManager(spec)) + }.let(ClosableCradleResource::class::cast) return resource.manager } @@ -137,7 +147,10 @@ public class Th2CradleExtension : } } - private fun startCradle(spec: CradleSpec, testInstance: Any) { + private fun startCradle( + spec: CradleSpec, + testInstance: Any, + ) { cradle = testInstance.getFieldOrDefault { CradleIntegration.defaultImage() } cradle.start() if (spec.reuseKeyspace) { @@ -153,9 +166,7 @@ public class Th2CradleExtension : } } - private fun createManager( - spec: CradleSpec, - ): CassandraCradleManager { + private fun createManager(spec: CradleSpec): CassandraCradleManager { val contactPoint = cradle.container.contactPoint return CassandraCradleManager( CassandraConnectionSettings( @@ -182,14 +193,19 @@ public class Th2CradleExtension : } } -private fun CradleStorage.createPages(bookName: String, spec: CradleSpec, minDistanceBetweenPages: Long) { +private fun CradleStorage.createPages( + bookName: String, + spec: CradleSpec, + minDistanceBetweenPages: Long, +) { val bookStart = Instant.now() - val bookInfo: BookInfo = addBook( - BookToAdd( - bookName, - bookStart, - ), - ) + val bookInfo: BookInfo = + addBook( + BookToAdd( + bookName, + bookStart, + ), + ) var pageStart: Instant = bookStart val pageEnd: Instant = pageStart + spec.autoPageInterval var index = 1 diff --git a/src/main/kotlin/com/exactpro/th2/test/extension/Th2CustomConfigExtension.kt b/src/main/kotlin/com/exactpro/th2/test/extension/Th2CustomConfigExtension.kt index 186c229..bc4226a 100644 --- a/src/main/kotlin/com/exactpro/th2/test/extension/Th2CustomConfigExtension.kt +++ b/src/main/kotlin/com/exactpro/th2/test/extension/Th2CustomConfigExtension.kt @@ -27,9 +27,15 @@ import org.junit.platform.commons.support.AnnotationSupport import org.junit.platform.commons.support.ReflectionSupport import kotlin.io.path.outputStream -public class Th2CustomConfigExtension : TestInstancePostProcessor, BeforeTestExecutionCallback { +public class Th2CustomConfigExtension : + TestInstancePostProcessor, + BeforeTestExecutionCallback { private var spec: CustomConfigSpec? = null - override fun postProcessTestInstance(testInstance: Any, context: ExtensionContext) { + + override fun postProcessTestInstance( + testInstance: Any, + context: ExtensionContext, + ) { val fields = testInstance::class.findFields().ifEmpty { return } spec = testInstance.getSingle(fields) } @@ -37,17 +43,21 @@ public class Th2CustomConfigExtension : TestInstancePostProcessor, BeforeTestExe override fun beforeTestExecution(context: ExtensionContext) { val testClass = context.requiredTestClass val testMethod = context.requiredTestMethod - val customConfigSpec: CustomConfigSpec = AnnotationSupport.findAnnotation(testMethod, CustomConfigProvider::class.java) - .map { - val configProvider = ReflectionSupport.findMethod( - testClass, - it.name, - ).orElseGet { error("cannot find method ${it.name} in class $testClass") } - val config = ReflectionSupport.invokeMethod(configProvider, context.requiredTestInstance) - (config as? CustomConfigSpec) ?: error("method $configProvider does not return ${CustomConfigSpec::class}") - }.orElseGet { - spec - } ?: return + val customConfigSpec: CustomConfigSpec = + AnnotationSupport + .findAnnotation(testMethod, CustomConfigProvider::class.java) + .map { + val configProvider = + ReflectionSupport + .findMethod( + testClass, + it.name, + ).orElseGet { error("cannot find method ${it.name} in class $testClass") } + val config = ReflectionSupport.invokeMethod(configProvider, context.requiredTestInstance) + (config as? CustomConfigSpec) ?: error("method $configProvider does not return ${CustomConfigSpec::class}") + }.orElseGet { + spec + } ?: return writeSpec(context, customConfigSpec) } diff --git a/src/main/kotlin/com/exactpro/th2/test/extension/Th2GrpcExtension.kt b/src/main/kotlin/com/exactpro/th2/test/extension/Th2GrpcExtension.kt index 87f65c9..b8f2e7e 100644 --- a/src/main/kotlin/com/exactpro/th2/test/extension/Th2GrpcExtension.kt +++ b/src/main/kotlin/com/exactpro/th2/test/extension/Th2GrpcExtension.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,10 +33,15 @@ import java.io.IOException import java.net.ServerSocket import kotlin.io.path.outputStream -public class Th2GrpcExtension : TestInstancePostProcessor, BeforeAllCallback { +public class Th2GrpcExtension : + TestInstancePostProcessor, + BeforeAllCallback { private var spec: GrpcSpec? = null - override fun postProcessTestInstance(testInstance: Any, context: ExtensionContext) { + override fun postProcessTestInstance( + testInstance: Any, + context: ExtensionContext, + ) { val fields = testInstance::class.findFields().ifEmpty { return } spec = testInstance.getSingle(fields) } @@ -76,21 +81,26 @@ public class Th2GrpcExtension : TestInstancePostProcessor, BeforeAllCallback { testPort: Int, ) = GrpcConfiguration( serverConfiguration = GrpcServerConfiguration(port = appPort), - services = grpcSpec.clients.associate { - "${it.type.simpleName}-${RandomStringUtils.randomAlphabetic(5)}" to GrpcServiceConfiguration( - strategy = RobinRoutingStrategy().apply { - init(GrpcRawRobinStrategy(endpoints = listOf("test-endpoint"))) - }, - serviceClass = it.type, - endpoints = mapOf( - "test-endpoint" to GrpcEndpointConfiguration( - host = "localhost", - port = testPort, - attributes = it.attributes.toList(), - ), - ), - ) - }, + services = + grpcSpec.clients.associate { + "${it.type.simpleName}-${RandomStringUtils.randomAlphabetic(5)}" to + GrpcServiceConfiguration( + strategy = + RobinRoutingStrategy().apply { + init(GrpcRawRobinStrategy(endpoints = listOf("test-endpoint"))) + }, + serviceClass = it.type, + endpoints = + mapOf( + "test-endpoint" to + GrpcEndpointConfiguration( + host = "localhost", + port = testPort, + attributes = it.attributes.toList(), + ), + ), + ) + }, ) private fun createTestGrpcConfig( @@ -99,23 +109,31 @@ public class Th2GrpcExtension : TestInstancePostProcessor, BeforeAllCallback { testPort: Int, ) = GrpcConfiguration( serverConfiguration = GrpcServerConfiguration(port = testPort), - services = grpcSpec.servers.associate { - "${it.simpleName}-${RandomStringUtils.randomAlphabetic(5)}" to GrpcServiceConfiguration( - strategy = RobinRoutingStrategy().apply { - init(GrpcRawRobinStrategy(endpoints = listOf("test-endpoint"))) - }, - serviceClass = it, - endpoints = mapOf( - "test-endpoint" to GrpcEndpointConfiguration( - host = "localhost", - port = appPort, - ), - ), - ) - }, + services = + grpcSpec.servers.associate { + "${it.simpleName}-${RandomStringUtils.randomAlphabetic(5)}" to + GrpcServiceConfiguration( + strategy = + RobinRoutingStrategy().apply { + init(GrpcRawRobinStrategy(endpoints = listOf("test-endpoint"))) + }, + serviceClass = it, + endpoints = + mapOf( + "test-endpoint" to + GrpcEndpointConfiguration( + host = "localhost", + port = appPort, + ), + ), + ) + }, ) - private fun findFreePort(start: Int = 1025, end: Int = 30000): Int { + private fun findFreePort( + start: Int = 1025, + end: Int = 30000, + ): Int { for (port in start..end) { try { ServerSocket(port).use { diff --git a/src/main/kotlin/com/exactpro/th2/test/extension/Th2RabbitMqExtension.kt b/src/main/kotlin/com/exactpro/th2/test/extension/Th2RabbitMqExtension.kt index 0fa2a3c..1108ff1 100644 --- a/src/main/kotlin/com/exactpro/th2/test/extension/Th2RabbitMqExtension.kt +++ b/src/main/kotlin/com/exactpro/th2/test/extension/Th2RabbitMqExtension.kt @@ -33,11 +33,18 @@ import java.lang.reflect.Field import java.nio.file.Path import kotlin.io.path.outputStream -public class Th2RabbitMqExtension : TestInstancePostProcessor, BeforeAllCallback, BeforeEachCallback, AfterAllCallback { +public class Th2RabbitMqExtension : + TestInstancePostProcessor, + BeforeAllCallback, + BeforeEachCallback, + AfterAllCallback { private var spec = RabbitMqSpec.create() private lateinit var rabbitmq: RabbitMqIntegration - override fun postProcessTestInstance(testInstance: Any, context: ExtensionContext) { + override fun postProcessTestInstance( + testInstance: Any, + context: ExtensionContext, + ) { val fields: List = testInstance::class.findFields().ifEmpty { return } spec = testInstance.getSingle(fields) } @@ -74,11 +81,13 @@ public class Th2RabbitMqExtension : TestInstancePostProcessor, BeforeAllCallback } private fun startMq(testInstance: Any) { - rabbitmq = testInstance.getFieldOrDefault { - RabbitMqIntegration.defaultImage().withDefaultExchange() - }.also { - RabbitMqConfigurator.setupQueues(it, spec) - } + rabbitmq = + testInstance + .getFieldOrDefault { + RabbitMqIntegration.defaultImage().withDefaultExchange() + }.also { + RabbitMqConfigurator.setupQueues(it, spec) + } rabbitmq.start() } diff --git a/src/main/kotlin/com/exactpro/th2/test/integration/ConfigurationWriter.kt b/src/main/kotlin/com/exactpro/th2/test/integration/ConfigurationWriter.kt index 8c8c9da..16cb94d 100644 --- a/src/main/kotlin/com/exactpro/th2/test/integration/ConfigurationWriter.kt +++ b/src/main/kotlin/com/exactpro/th2/test/integration/ConfigurationWriter.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,10 @@ internal object ConfigurationWriter { const val GRPC_CONFIG: String = "grpc.json" const val GRPC_ROUTER_CONFIG: String = "grpc_router.json" - fun write(cfg: Any, output: OutputStream) { + fun write( + cfg: Any, + output: OutputStream, + ) { MAPPER.writeValue(output, cfg) } } diff --git a/src/main/kotlin/com/exactpro/th2/test/integration/CradleConfigProvider.kt b/src/main/kotlin/com/exactpro/th2/test/integration/CradleConfigProvider.kt index 406eb75..bab0bac 100644 --- a/src/main/kotlin/com/exactpro/th2/test/integration/CradleConfigProvider.kt +++ b/src/main/kotlin/com/exactpro/th2/test/integration/CradleConfigProvider.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,10 @@ import com.exactpro.th2.test.spec.CradleSpec import com.fasterxml.jackson.annotation.JsonUnwrapped internal object CradleConfigProvider { - fun getCradleConnectionConfig(cradle: CradleIntegration, spec: CradleSpec): CradleConfidentialConfiguration { + fun getCradleConnectionConfig( + cradle: CradleIntegration, + spec: CradleSpec, + ): CradleConfidentialConfiguration { val contactPoint = cradle.container.contactPoint return CradleConfidentialConfiguration( host = contactPoint.hostName, @@ -34,7 +37,10 @@ internal object CradleConfigProvider { ) } - fun getCradleParametersConfig(spec: CradleSpec, prepareStorage: Boolean = false): CombinedCassandraStorageSettings { + fun getCradleParametersConfig( + spec: CradleSpec, + prepareStorage: Boolean = false, + ): CombinedCassandraStorageSettings { // this is done to group both configuration in one object // change it when the common factory will distinguish between them return CombinedCassandraStorageSettings(prepareStorage, spec.settings) diff --git a/src/main/kotlin/com/exactpro/th2/test/integration/CradleConfigurator.kt b/src/main/kotlin/com/exactpro/th2/test/integration/CradleConfigurator.kt index 002a653..95c8c82 100644 --- a/src/main/kotlin/com/exactpro/th2/test/integration/CradleConfigurator.kt +++ b/src/main/kotlin/com/exactpro/th2/test/integration/CradleConfigurator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,10 @@ package com.exactpro.th2.test.integration import com.exactpro.th2.test.spec.CradleSpec internal object CradleConfigurator { - fun configureCradle(integration: CradleIntegration, spec: CradleSpec) { + fun configureCradle( + integration: CradleIntegration, + spec: CradleSpec, + ) { integration.recreateKeyspace(spec.settings.keyspace) } } diff --git a/src/main/kotlin/com/exactpro/th2/test/integration/CradleIntegration.kt b/src/main/kotlin/com/exactpro/th2/test/integration/CradleIntegration.kt index b3551d7..e26e66e 100644 --- a/src/main/kotlin/com/exactpro/th2/test/integration/CradleIntegration.kt +++ b/src/main/kotlin/com/exactpro/th2/test/integration/CradleIntegration.kt @@ -27,9 +27,10 @@ import java.util.function.Consumer public class CradleIntegration private constructor( cassandraImageName: DockerImageName, ) : Startable { - internal val container = CassandraContainer(cassandraImageName).apply { - withLogConsumer(Slf4jLogConsumer(LoggerFactory.getLogger(CradleIntegration::class.java)).withSeparateOutputStreams()) - } + internal val container = + CassandraContainer(cassandraImageName).apply { + withLogConsumer(Slf4jLogConsumer(LoggerFactory.getLogger(CradleIntegration::class.java)).withSeparateOutputStreams()) + } override fun start() { container.start() @@ -39,15 +40,18 @@ public class CradleIntegration private constructor( container.stop() } - public fun configureContainer(block: Consumer): CradleIntegration = apply { - block.accept(container) - } + public fun configureContainer(block: Consumer): CradleIntegration = + apply { + block.accept(container) + } internal fun recreateKeyspace(keyspace: String) { - CqlSession.builder() + CqlSession + .builder() .addContactPoint(container.contactPoint) .withLocalDatacenter(container.localDatacenter) - .build().use { + .build() + .use { it.execute("DROP KEYSPACE IF EXISTS $keyspace") it.execute("CREATE KEYSPACE $keyspace WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1};") } @@ -61,10 +65,8 @@ public class CradleIntegration private constructor( public fun defaultImage(): CradleIntegration = CradleIntegration(DEFAULT_IMAGE) @JvmStatic - public fun fromImage(imageName: DockerImageName): CradleIntegration = - CradleIntegration(imageName) + public fun fromImage(imageName: DockerImageName): CradleIntegration = CradleIntegration(imageName) } } -public fun CradleIntegration.container(block: CassandraContainer.() -> Unit): CradleIntegration = - configureContainer(Consumer(block)) +public fun CradleIntegration.container(block: CassandraContainer.() -> Unit): CradleIntegration = configureContainer(Consumer(block)) diff --git a/src/main/kotlin/com/exactpro/th2/test/integration/RabbitMqConfigProvider.kt b/src/main/kotlin/com/exactpro/th2/test/integration/RabbitMqConfigProvider.kt index efa143b..c9f1fde 100644 --- a/src/main/kotlin/com/exactpro/th2/test/integration/RabbitMqConfigProvider.kt +++ b/src/main/kotlin/com/exactpro/th2/test/integration/RabbitMqConfigProvider.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,9 +25,8 @@ import com.exactpro.th2.test.spec.PinSpec import com.exactpro.th2.test.spec.RabbitMqSpec internal object RabbitMqConfigProvider { - - fun getConnectionConfig(rabbitmq: RabbitMqIntegration): RabbitMQConfiguration { - return RabbitMQConfiguration( + fun getConnectionConfig(rabbitmq: RabbitMqIntegration): RabbitMQConfiguration = + RabbitMQConfiguration( host = rabbitmq.container.host, vHost = "", port = rabbitmq.container.amqpPort, @@ -35,19 +34,20 @@ internal object RabbitMqConfigProvider { password = rabbitmq.container.adminPassword, exchangeName = RabbitMqIntegration.DEFAULT_EXCHANGE, ) - } /** * Returns [MessageRouterConfiguration] that can be used by component under the test */ fun getComponentConfig(spec: RabbitMqSpec): MessageRouterConfiguration { - val publishers = spec.pinsSpec.publishers.pins.mapValues { (name, spec) -> - pinSpecToConfiguration(name, spec, isPublisher = true) - } + val publishers = + spec.pinsSpec.publishers.pins.mapValues { (name, spec) -> + pinSpecToConfiguration(name, spec, isPublisher = true) + } - val subscribers = spec.pinsSpec.subscribers.pins.mapValues { (name, spec) -> - pinSpecToConfiguration(name, spec, isPublisher = false) - } + val subscribers = + spec.pinsSpec.subscribers.pins.mapValues { (name, spec) -> + pinSpecToConfiguration(name, spec, isPublisher = false) + } return MessageRouterConfiguration( queues = publishers + subscribers, @@ -58,31 +58,36 @@ internal object RabbitMqConfigProvider { * Returns [MessageRouterConfiguration] that can be used to send/receive message to component under the test */ fun getTestConfig(spec: RabbitMqSpec): MessageRouterConfiguration { - val queuesForPublishers = spec.pinsSpec.publishers.pins.mapValues { (name, spec) -> - pinSpecToConfiguration( - name, - spec, - isPublisher = false, - ignoreFilters = true, - attributes = spec.attributeSet.replaceValues( - QueueAttribute.PUBLISH.value to QueueAttribute.SUBSCRIBE.value, - ) + listOf(name), - ) - } + val queuesForPublishers = + spec.pinsSpec.publishers.pins.mapValues { (name, spec) -> + pinSpecToConfiguration( + name, + spec, + isPublisher = false, + ignoreFilters = true, + attributes = + spec.attributeSet.replaceValues( + QueueAttribute.PUBLISH.value to QueueAttribute.SUBSCRIBE.value, + ) + listOf(name), + ) + } - val routingForSubscribers = spec.pinsSpec.subscribers.pins.mapValues { (name, spec) -> - pinSpecToConfiguration( - name, - spec, - isPublisher = true, - ignoreFilters = true, - attributes = spec.attributeSet.replaceValues( - QueueAttribute.SUBSCRIBE.value to QueueAttribute.PUBLISH.value, - ) + listOf(name), - ) - } - val queues: MutableMap = (queuesForPublishers + routingForSubscribers) - .toMutableMap() + val routingForSubscribers = + spec.pinsSpec.subscribers.pins.mapValues { (name, spec) -> + pinSpecToConfiguration( + name, + spec, + isPublisher = true, + ignoreFilters = true, + attributes = + spec.attributeSet.replaceValues( + QueueAttribute.SUBSCRIBE.value to QueueAttribute.PUBLISH.value, + ) + listOf(name), + ) + } + val queues: MutableMap = + (queuesForPublishers + routingForSubscribers) + .toMutableMap() addEventRoutingForTestFactory(queues) return MessageRouterConfiguration( queues = queues, @@ -91,26 +96,25 @@ internal object RabbitMqConfigProvider { private fun addEventRoutingForTestFactory(queues: MutableMap) { val key = "events-${System.currentTimeMillis()}" - queues[key] = QueueConfiguration( - routingKey = key, - queue = "", - exchange = RabbitMqIntegration.DEFAULT_EXCHANGE, - attributes = listOf(QueueAttribute.PUBLISH.value, QueueAttribute.EVENT.value), - isWritable = true, - isReadable = false, - ) + queues[key] = + QueueConfiguration( + routingKey = key, + queue = "", + exchange = RabbitMqIntegration.DEFAULT_EXCHANGE, + attributes = listOf(QueueAttribute.PUBLISH.value, QueueAttribute.EVENT.value), + isWritable = true, + isReadable = false, + ) } - private fun Set.replaceValues( - vararg pairs: Pair, - ): List { - return toMutableSet().apply { - for ((current, new) in pairs) { - remove(current) - add(new) - } - }.toList() - } + private fun Set.replaceValues(vararg pairs: Pair): List = + toMutableSet() + .apply { + for ((current, new) in pairs) { + remove(current) + add(new) + } + }.toList() private fun pinSpecToConfiguration( name: String, @@ -125,11 +129,12 @@ internal object RabbitMqConfigProvider { attributes = attributes, isReadable = !isPublisher, isWritable = isPublisher, - filters = spec.filters.takeUnless { ignoreFilters }?.map { - MqRouterFilterConfiguration( - it.message.filters, - it.metadata.filters, - ) - } ?: emptyList(), + filters = + spec.filters.takeUnless { ignoreFilters }?.map { + MqRouterFilterConfiguration( + it.message.filters, + it.metadata.filters, + ) + } ?: emptyList(), ) } diff --git a/src/main/kotlin/com/exactpro/th2/test/integration/RabbitMqIntegration.kt b/src/main/kotlin/com/exactpro/th2/test/integration/RabbitMqIntegration.kt index c65554e..87cc2fb 100644 --- a/src/main/kotlin/com/exactpro/th2/test/integration/RabbitMqIntegration.kt +++ b/src/main/kotlin/com/exactpro/th2/test/integration/RabbitMqIntegration.kt @@ -28,8 +28,9 @@ public class RabbitMqIntegration private constructor( rabbitMqImageName: DockerImageName, ) : Startable { private val logger = KotlinLogging.logger { } - internal val container = RabbitMQContainer(rabbitMqImageName) - .withLogConsumer(Slf4jLogConsumer(LoggerFactory.getLogger(RabbitMqIntegration::class.java)).withSeparateOutputStreams()) + internal val container = + RabbitMQContainer(rabbitMqImageName) + .withLogConsumer(Slf4jLogConsumer(LoggerFactory.getLogger(RabbitMqIntegration::class.java)).withSeparateOutputStreams()) override fun start() { container.start() @@ -39,51 +40,58 @@ public class RabbitMqIntegration private constructor( container.stop() } - public fun configureContainer(block: Consumer): RabbitMqIntegration = apply { - block.accept(container) - } + public fun configureContainer(block: Consumer): RabbitMqIntegration = + apply { + block.accept(container) + } - public fun withDurableExchange(): RabbitMqIntegration = apply { - withExchange(true) - } + public fun withDurableExchange(): RabbitMqIntegration = + apply { + withExchange(true) + } - public fun withDefaultExchange(): RabbitMqIntegration = apply { - withExchange(false) - } + public fun withDefaultExchange(): RabbitMqIntegration = + apply { + withExchange(false) + } @JvmOverloads public fun withQueue( queueName: String, durable: Boolean = false, - ): RabbitMqIntegration = apply { - container.withQueue(queueName, false, durable, emptyMap()) - } + ): RabbitMqIntegration = + apply { + container.withQueue(queueName, false, durable, emptyMap()) + } public fun withBinding( queueName: String, routingKey: String, - ): RabbitMqIntegration = apply { - container.withBinding( - DEFAULT_EXCHANGE, - queueName, - emptyMap(), - routingKey, - QUEUE_DESTINATION_TYPE, - ) - } - - public fun purgeQueue(queueName: String): RabbitMqIntegration = apply { - check(container.isRunning) { "cannot clear queue in not running container" } - val result = container.execInContainer( - "rabbitmqadmin", - "purge", - "queue", - "name=$queueName", - ) - check(result.exitCode == 0) { "cannot purge queue $queueName: ${result.stderr}" } - logger.debug { "Purge output: ${result.stdout}" } - logger.debug { "Purge error: ${result.stderr}" } - } + ): RabbitMqIntegration = + apply { + container.withBinding( + DEFAULT_EXCHANGE, + queueName, + emptyMap(), + routingKey, + QUEUE_DESTINATION_TYPE, + ) + } + + public fun purgeQueue(queueName: String): RabbitMqIntegration = + apply { + check(container.isRunning) { "cannot clear queue in not running container" } + val result = + container.execInContainer( + "rabbitmqadmin", + "purge", + "queue", + "name=$queueName", + ) + check(result.exitCode == 0) { "cannot purge queue $queueName: ${result.stderr}" } + logger.debug { "Purge output: ${result.stdout}" } + logger.debug { "Purge error: ${result.stderr}" } + } private fun withExchange(durable: Boolean) { container.withExchange( @@ -102,8 +110,7 @@ public class RabbitMqIntegration private constructor( private const val QUEUE_DESTINATION_TYPE: String = "queue" @JvmStatic - public fun fromImage(imageName: String): RabbitMqIntegration = - RabbitMqIntegration(DockerImageName.parse(imageName)) + public fun fromImage(imageName: String): RabbitMqIntegration = RabbitMqIntegration(DockerImageName.parse(imageName)) @JvmStatic public fun defaultImage(): RabbitMqIntegration = RabbitMqIntegration(DEFAULT_IMAGE) @@ -114,5 +121,4 @@ public class RabbitMqIntegration private constructor( } } -public fun RabbitMqIntegration.container(block: RabbitMQContainer.() -> Unit): RabbitMqIntegration = - configureContainer(Consumer(block)) +public fun RabbitMqIntegration.container(block: RabbitMQContainer.() -> Unit): RabbitMqIntegration = configureContainer(Consumer(block)) diff --git a/src/main/kotlin/com/exactpro/th2/test/queue/CollectorMessageListener.kt b/src/main/kotlin/com/exactpro/th2/test/queue/CollectorMessageListener.kt index 5ae345b..2f14f69 100644 --- a/src/main/kotlin/com/exactpro/th2/test/queue/CollectorMessageListener.kt +++ b/src/main/kotlin/com/exactpro/th2/test/queue/CollectorMessageListener.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,13 +26,16 @@ import java.util.concurrent.TimeUnit public class CollectorMessageListener private constructor( private val queue: BlockingQueue, -) : MessageListener, Iterable by queue { - +) : MessageListener, + Iterable by queue { public fun isEmpty(): Boolean = queue.isEmpty() public fun poll(duration: Duration): T? = queue.poll(duration.toMillis(), TimeUnit.MILLISECONDS) - override fun handle(deliveryMetadata: DeliveryMetadata, message: T) { + override fun handle( + deliveryMetadata: DeliveryMetadata, + message: T, + ) { queue.put(message) } @@ -42,8 +45,7 @@ public class CollectorMessageListener private constructor( CollectorMessageListener(ArrayBlockingQueue(capacity)) @JvmStatic - public fun createUnbound(): CollectorMessageListener = - CollectorMessageListener(LinkedBlockingQueue()) + public fun createUnbound(): CollectorMessageListener = CollectorMessageListener(LinkedBlockingQueue()) } } diff --git a/src/main/kotlin/com/exactpro/th2/test/spec/CradleSpec.kt b/src/main/kotlin/com/exactpro/th2/test/spec/CradleSpec.kt index a300bf4..adb08a6 100644 --- a/src/main/kotlin/com/exactpro/th2/test/spec/CradleSpec.kt +++ b/src/main/kotlin/com/exactpro/th2/test/spec/CradleSpec.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,43 +39,50 @@ public class CradleSpec private constructor( settings.resultPageSize = 2 } - public fun withPageSize(size: Int): CradleSpec = apply { - require(size > 1) { "page size must be greater than 1" } - settings.resultPageSize = size - } + public fun withPageSize(size: Int): CradleSpec = + apply { + require(size > 1) { "page size must be greater than 1" } + settings.resultPageSize = size + } - public fun withRefreshBookInterval(intervalMillis: Long): CradleSpec = apply { - settings.bookRefreshIntervalMillis = intervalMillis - } + public fun withRefreshBookInterval(intervalMillis: Long): CradleSpec = + apply { + settings.bookRefreshIntervalMillis = intervalMillis + } - public fun withQueryTimeout(timeout: Long): CradleSpec = apply { - require(timeout > 0) { "timeout must be positive" } - settings.timeout = timeout - } + public fun withQueryTimeout(timeout: Long): CradleSpec = + apply { + require(timeout > 0) { "timeout must be positive" } + settings.timeout = timeout + } - public fun disableAutoPages(): CradleSpec = apply { - autoPages = false - } + public fun disableAutoPages(): CradleSpec = + apply { + autoPages = false + } - public fun withAutoPageInterval(interval: Duration): CradleSpec = apply { - require(interval > Duration.ZERO) { "interval must be positive" } - require(interval.toMillis() > settings.calculatePageActionRejectionThreshold()) { - "interval must be longer than bookRefreshIntervalMillis ${settings.bookRefreshIntervalMillis}" + public fun withAutoPageInterval(interval: Duration): CradleSpec = + apply { + require(interval > Duration.ZERO) { "interval must be positive" } + require(interval.toMillis() > settings.calculatePageActionRejectionThreshold()) { + "interval must be longer than bookRefreshIntervalMillis ${settings.bookRefreshIntervalMillis}" + } + checkPageDurationAndInterval() + autoPageInterval = interval } - checkPageDurationAndInterval() - autoPageInterval = interval - } - public fun withPageDuration(duration: Duration): CradleSpec = apply { - require(duration > Duration.ZERO) { "duration must be positive" } - require(duration.toMillis() > settings.calculatePageActionRejectionThreshold()) - checkPageDurationAndInterval() - pageDuration = duration - } + public fun withPageDuration(duration: Duration): CradleSpec = + apply { + require(duration > Duration.ZERO) { "duration must be positive" } + require(duration.toMillis() > settings.calculatePageActionRejectionThreshold()) + checkPageDurationAndInterval() + pageDuration = duration + } - public fun reuseKeyspace(): CradleSpec = apply { - reuseKeyspace = true - } + public fun reuseKeyspace(): CradleSpec = + apply { + reuseKeyspace = true + } private fun checkPageDurationAndInterval() { require(pageDuration <= autoPageInterval) { diff --git a/src/main/kotlin/com/exactpro/th2/test/spec/CustomConfigSpec.kt b/src/main/kotlin/com/exactpro/th2/test/spec/CustomConfigSpec.kt index f714fc4..2b74bf6 100644 --- a/src/main/kotlin/com/exactpro/th2/test/spec/CustomConfigSpec.kt +++ b/src/main/kotlin/com/exactpro/th2/test/spec/CustomConfigSpec.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,8 +24,7 @@ public class CustomConfigSpec private constructor( ) { public companion object { @JvmStatic - public fun fromString(content: String): CustomConfigSpec = - CustomConfigSpec { content } + public fun fromString(content: String): CustomConfigSpec = CustomConfigSpec { content } @JvmStatic public fun fromObject(obj: Any): CustomConfigSpec { @@ -34,7 +33,6 @@ public class CustomConfigSpec private constructor( } @JvmStatic - public fun fromSupplier(supplier: Supplier): CustomConfigSpec = - CustomConfigSpec(supplier) + public fun fromSupplier(supplier: Supplier): CustomConfigSpec = CustomConfigSpec(supplier) } } diff --git a/src/main/kotlin/com/exactpro/th2/test/spec/GrpcSpec.kt b/src/main/kotlin/com/exactpro/th2/test/spec/GrpcSpec.kt index 7309d06..0b59f7d 100644 --- a/src/main/kotlin/com/exactpro/th2/test/spec/GrpcSpec.kt +++ b/src/main/kotlin/com/exactpro/th2/test/spec/GrpcSpec.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,17 +25,23 @@ public class GrpcSpec private constructor() { internal val servers: MutableSet> = hashSetOf() internal val routerSpec = RouterConfigurationSpec() - public fun registerClient(clientClass: Class<*>, vararg attributes: String): GrpcSpec = apply { - clients += Client(clientClass, attributes.toSet()) - } + public fun registerClient( + clientClass: Class<*>, + vararg attributes: String, + ): GrpcSpec = + apply { + clients += Client(clientClass, attributes.toSet()) + } - public fun registerServer(serverClass: Class<*>): GrpcSpec = apply { - servers += serverClass - } + public fun registerServer(serverClass: Class<*>): GrpcSpec = + apply { + servers += serverClass + } - public fun configureRouter(block: Consumer): GrpcSpec = apply { - block.accept(routerSpec) - } + public fun configureRouter(block: Consumer): GrpcSpec = + apply { + block.accept(routerSpec) + } internal class Client( val type: Class<*>, @@ -51,45 +57,54 @@ public class GrpcSpec private constructor() { public class RouterConfigurationSpec internal constructor() { internal val routerConfig = GrpcRouterConfiguration() - public fun enableSizeMeasuring(): RouterConfigurationSpec = apply { - routerConfig.enableSizeMeasuring = true - } - - public fun withMaxRetries(count: Int): RouterConfigurationSpec = apply { - routerConfig.retryConfiguration = routerConfig.retryConfiguration.copy( - maxAttempts = count, - ) - } - - public fun withMinDelay(delayMillis: Long): RouterConfigurationSpec = apply { - routerConfig.retryConfiguration = routerConfig.retryConfiguration.copy( - minMethodRetriesTimeout = delayMillis, - ) - } - - public fun withMinDelay(delay: Duration): RouterConfigurationSpec = apply { - routerConfig.retryConfiguration = routerConfig.retryConfiguration.copy( - minMethodRetriesTimeout = delay.toMillis(), - ) - } - - public fun withMaxDelay(delayMillis: Long): RouterConfigurationSpec = apply { - routerConfig.retryConfiguration = routerConfig.retryConfiguration.copy( - maxMethodRetriesTimeout = delayMillis, - ) - } - - public fun withMaxDelay(delay: Duration): RouterConfigurationSpec = apply { - routerConfig.retryConfiguration = routerConfig.retryConfiguration.copy( - maxMethodRetriesTimeout = delay.toMillis(), - ) - } + public fun enableSizeMeasuring(): RouterConfigurationSpec = + apply { + routerConfig.enableSizeMeasuring = true + } + + public fun withMaxRetries(count: Int): RouterConfigurationSpec = + apply { + routerConfig.retryConfiguration = + routerConfig.retryConfiguration.copy( + maxAttempts = count, + ) + } + + public fun withMinDelay(delayMillis: Long): RouterConfigurationSpec = + apply { + routerConfig.retryConfiguration = + routerConfig.retryConfiguration.copy( + minMethodRetriesTimeout = delayMillis, + ) + } + + public fun withMinDelay(delay: Duration): RouterConfigurationSpec = + apply { + routerConfig.retryConfiguration = + routerConfig.retryConfiguration.copy( + minMethodRetriesTimeout = delay.toMillis(), + ) + } + + public fun withMaxDelay(delayMillis: Long): RouterConfigurationSpec = + apply { + routerConfig.retryConfiguration = + routerConfig.retryConfiguration.copy( + maxMethodRetriesTimeout = delayMillis, + ) + } + + public fun withMaxDelay(delay: Duration): RouterConfigurationSpec = + apply { + routerConfig.retryConfiguration = + routerConfig.retryConfiguration.copy( + maxMethodRetriesTimeout = delay.toMillis(), + ) + } } -public inline fun GrpcSpec.client(vararg attributes: String): GrpcSpec = - registerClient(T::class.java, *attributes) +public inline fun GrpcSpec.client(vararg attributes: String): GrpcSpec = registerClient(T::class.java, *attributes) public inline fun GrpcSpec.server(): GrpcSpec = registerServer(T::class.java) -public fun GrpcSpec.routerConfig(block: RouterConfigurationSpec.() -> Unit): GrpcSpec = - configureRouter(Consumer(block)) +public fun GrpcSpec.routerConfig(block: RouterConfigurationSpec.() -> Unit): GrpcSpec = configureRouter(Consumer(block)) diff --git a/src/main/kotlin/com/exactpro/th2/test/spec/RabbitMqSpec.kt b/src/main/kotlin/com/exactpro/th2/test/spec/RabbitMqSpec.kt index 4878ed9..0dc41e8 100644 --- a/src/main/kotlin/com/exactpro/th2/test/spec/RabbitMqSpec.kt +++ b/src/main/kotlin/com/exactpro/th2/test/spec/RabbitMqSpec.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,25 +27,26 @@ private annotation class RabbitMqSpecDsl @RabbitMqSpecDsl public class RabbitMqSpec private constructor() { - internal val pinsSpec: PinsSpec = PinsSpec() - public fun configurePins(block: Consumer): RabbitMqSpec = apply { - block.accept(pinsSpec) - } + public fun configurePins(block: Consumer): RabbitMqSpec = + apply { + block.accept(pinsSpec) + } public companion object { public const val EVENTS_PIN_NAME: String = "events" @JvmStatic - public fun create(): RabbitMqSpec = RabbitMqSpec() - .pins { - publishers { - pin(EVENTS_PIN_NAME) { - attributes("event") + public fun create(): RabbitMqSpec = + RabbitMqSpec() + .pins { + publishers { + pin(EVENTS_PIN_NAME) { + attributes("event") + } } } - } } } @@ -79,31 +80,40 @@ public class PinCollectionSpec internal constructor( ) { internal val pins: MutableMap = hashMapOf() - public fun configurePin(name: String, block: Consumer): PinCollectionSpec = apply { - val pinSpec = PinSpec().also(block::accept).apply { - attributeSet.addAll(this@PinCollectionSpec.defaultAttributes) - } - check(pins.put(name, pinSpec) == null) { - "duplicated pin $name" + public fun configurePin( + name: String, + block: Consumer, + ): PinCollectionSpec = + apply { + val pinSpec = + PinSpec().also(block::accept).apply { + attributeSet.addAll(this@PinCollectionSpec.defaultAttributes) + } + check(pins.put(name, pinSpec) == null) { + "duplicated pin $name" + } } - } } -public fun PinCollectionSpec.pin(name: String, block: PinSpec.() -> Unit): PinCollectionSpec = - configurePin(name, Consumer(block)) +public fun PinCollectionSpec.pin( + name: String, + block: PinSpec.() -> Unit, +): PinCollectionSpec = configurePin(name, Consumer(block)) @RabbitMqSpecDsl public class PinSpec internal constructor() { internal val attributeSet: MutableSet = hashSetOf() internal val filters: MutableList = arrayListOf() - public fun attributes(vararg attributes: String): PinSpec = apply { - attributeSet.addAll(attributes) - } + public fun attributes(vararg attributes: String): PinSpec = + apply { + attributeSet.addAll(attributes) + } - public fun addFilter(block: Consumer): PinSpec = apply { - filters.add(FilterContainerSpec().also(block::accept)) - } + public fun addFilter(block: Consumer): PinSpec = + apply { + filters.add(FilterContainerSpec().also(block::accept)) + } } public fun PinSpec.filter(block: FilterContainerSpec.() -> Unit): PinSpec = addFilter(Consumer(block)) @@ -123,6 +133,7 @@ public class FilterContainerSpec internal constructor() { } public fun FilterContainerSpec.metadata(block: FilterSpec.() -> Unit): Unit = configureMetadataFilter(Consumer(block)) + public fun FilterContainerSpec.message(block: FilterSpec.() -> Unit): Unit = configureMessageFilter(Consumer(block)) @RabbitMqSpecDsl @@ -148,28 +159,26 @@ public class FilterSpec internal constructor() { private val filters: MutableList, private val root: FilterSpec, ) { + public fun shouldBeEmpty(): FilterSpec = addFilter(FieldFilterOperation.EMPTY) - public fun shouldBeEmpty(): FilterSpec = - addFilter(FieldFilterOperation.EMPTY) - - public fun shouldNotBeEmpty(): FilterSpec = - addFilter(FieldFilterOperation.NOT_EMPTY) + public fun shouldNotBeEmpty(): FilterSpec = addFilter(FieldFilterOperation.NOT_EMPTY) - public infix fun shouldBeEqualTo(expectedValue: String): FilterSpec = - addFilter(FieldFilterOperation.EQUAL, expectedValue) + public infix fun shouldBeEqualTo(expectedValue: String): FilterSpec = addFilter(FieldFilterOperation.EQUAL, expectedValue) - public infix fun shouldNotBeEqualTo(expectedValue: String): FilterSpec = - addFilter(FieldFilterOperation.NOT_EQUAL, expectedValue) + public infix fun shouldNotBeEqualTo(expectedValue: String): FilterSpec = addFilter(FieldFilterOperation.NOT_EQUAL, expectedValue) - public infix fun shouldMatchWildcard(wildcard: String): FilterSpec = - addFilter(FieldFilterOperation.WILDCARD, wildcard) + public infix fun shouldMatchWildcard(wildcard: String): FilterSpec = addFilter(FieldFilterOperation.WILDCARD, wildcard) - private fun addFilter(operation: FieldFilterOperation, expectedValue: String? = null): FilterSpec { - filters += FieldFilterConfiguration( - fieldName = fieldName, - expectedValue = expectedValue, - operation = operation, - ) + private fun addFilter( + operation: FieldFilterOperation, + expectedValue: String? = null, + ): FilterSpec { + filters += + FieldFilterConfiguration( + fieldName = fieldName, + expectedValue = expectedValue, + operation = operation, + ) return root } } diff --git a/src/test/kotlin/com/exactpro/th2/test/extension/TestExtension.kt b/src/test/kotlin/com/exactpro/th2/test/extension/TestExtension.kt index 42d1bec..d3df7f4 100644 --- a/src/test/kotlin/com/exactpro/th2/test/extension/TestExtension.kt +++ b/src/test/kotlin/com/exactpro/th2/test/extension/TestExtension.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,30 +41,35 @@ import java.time.Duration internal class TestExtension { @Suppress("unused") @JvmField - internal val spec: RabbitMqSpec = RabbitMqSpec.create() - .pins { - subscribers { - pin("test") { - attributes("transport-group") + internal val spec: RabbitMqSpec = + RabbitMqSpec + .create() + .pins { + subscribers { + pin("test") { + attributes("transport-group") + } } } - } @JvmField - internal val cradle: CradleSpec = CradleSpec.create("test") - .reuseKeyspace() - .withPageDuration(Duration.ofMinutes(1)) - .withAutoPageInterval(Duration.ofMinutes(15)) - .withRefreshBookInterval(100) + internal val cradle: CradleSpec = + CradleSpec + .create("test") + .reuseKeyspace() + .withPageDuration(Duration.ofMinutes(1)) + .withAutoPageInterval(Duration.ofMinutes(15)) + .withRefreshBookInterval(100) @JvmField - internal val customConfigSpec: CustomConfigSpec = CustomConfigSpec.fromString( - """ - { - "test": 42 - } - """.trimIndent(), - ) + internal val customConfigSpec: CustomConfigSpec = + CustomConfigSpec.fromString( + """ + { + "test": 42 + } + """.trimIndent(), + ) @Test fun testFactoryCanSendMessagesToApp( @@ -78,24 +83,25 @@ internal class TestExtension { monitor.unsubscribe() } - val groupBatch = GroupBatch.builder() - .setBook("book") - .setSessionGroup("group") - .addGroup( - MessageGroup.builder() - .addMessage( - ParsedMessage.builder() - .setId(MessageId.DEFAULT) - .setType("test") - .apply { - bodyBuilder() - .put("test", "A") - } - .build(), - ) - .build(), - ) - .build() + val groupBatch = + GroupBatch + .builder() + .setBook("book") + .setSessionGroup("group") + .addGroup( + MessageGroup + .builder() + .addMessage( + ParsedMessage + .builder() + .setId(MessageId.DEFAULT) + .setType("test") + .apply { + bodyBuilder() + .put("test", "A") + }.build(), + ).build(), + ).build() testFactory.transportGroupBatchRouter.send(groupBatch) val batch = listener.poll(Duration.ofSeconds(5)) diff --git a/src/test/kotlin/com/exactpro/th2/test/spec/TestCustomConfigSpec.kt b/src/test/kotlin/com/exactpro/th2/test/spec/TestCustomConfigSpec.kt index eb130b2..ec47b7e 100644 --- a/src/test/kotlin/com/exactpro/th2/test/spec/TestCustomConfigSpec.kt +++ b/src/test/kotlin/com/exactpro/th2/test/spec/TestCustomConfigSpec.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 Exactpro (Exactpro Systems Limited) + * Copyright 2024-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,13 +29,14 @@ import org.junit.jupiter.api.Test internal class TestCustomConfigSpec { private var testValue: Int = 0 - val custom = CustomConfigSpec.fromSupplier { - """ - { - "test": "$testValue" + val custom = + CustomConfigSpec.fromSupplier { + """ + { + "test": "$testValue" + } + """.trimIndent() } - """.trimIndent() - } @BeforeEach fun setUp() { @@ -59,8 +60,9 @@ internal class TestCustomConfigSpec { Assertions.assertEquals(54, setting.test, "unexpected value in property") } - fun config(): CustomConfigSpec = - CustomConfigSpec.fromObject(Setting(54)) + fun config(): CustomConfigSpec = CustomConfigSpec.fromObject(Setting(54)) - private data class Setting(val test: Int) + private data class Setting( + val test: Int, + ) } diff --git a/src/test/kotlin/com/exactpro/th2/test/spec/TestGrpcSpec.kt b/src/test/kotlin/com/exactpro/th2/test/spec/TestGrpcSpec.kt index 7f9466a..47eab83 100644 --- a/src/test/kotlin/com/exactpro/th2/test/spec/TestGrpcSpec.kt +++ b/src/test/kotlin/com/exactpro/th2/test/spec/TestGrpcSpec.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,23 +36,27 @@ internal class TestGrpcSpec { @Th2IntegrationTest inner class Service { @JvmField - internal val grpc = GrpcSpec.create() - .client() + internal val grpc = + GrpcSpec + .create() + .client() @Test fun `registers grpc service`( @Th2AppFactory factory: CommonFactory, @Th2TestFactory testFactory: CommonFactory, ) { - testFactory.grpcRouter.startServer( - TestCheck1Service(), - ).start() + testFactory.grpcRouter + .startServer( + TestCheck1Service(), + ).start() val check1Service = factory.grpcRouter.getService(Check1Service::class.java) - val response = check1Service.createCheckpoint( - CheckpointRequest.getDefaultInstance(), - ) + val response = + check1Service.createCheckpoint( + CheckpointRequest.getDefaultInstance(), + ) val sessionAliasToDirectionCheckpoint = response.checkpoint.bookNameToSessionAliasToDirectionCheckpointMap["test_book"] Assertions.assertNotNull(sessionAliasToDirectionCheckpoint, "cannot find book test_book") @@ -73,23 +77,27 @@ internal class TestGrpcSpec { @Th2IntegrationTest inner class Server { @JvmField - internal val grpc = GrpcSpec.create() - .server() + internal val grpc = + GrpcSpec + .create() + .server() @Test fun `registers grpc server`( @Th2AppFactory factory: CommonFactory, @Th2TestFactory testFactory: CommonFactory, ) { - factory.grpcRouter.startServer( - TestCheck1Service(), - ).start() + factory.grpcRouter + .startServer( + TestCheck1Service(), + ).start() val check1Service = testFactory.grpcRouter.getService(Check1Service::class.java) - val response = check1Service.createCheckpoint( - CheckpointRequest.getDefaultInstance(), - ) + val response = + check1Service.createCheckpoint( + CheckpointRequest.getDefaultInstance(), + ) val sessionAliasToDirectionCheckpoint = response.checkpoint.bookNameToSessionAliasToDirectionCheckpointMap["test_book"] Assertions.assertNotNull(sessionAliasToDirectionCheckpoint, "cannot find book test_book") @@ -112,25 +120,27 @@ private class TestCheck1Service : Check1Grpc.Check1ImplBase() { request: CheckpointRequest, responseObserver: StreamObserver, ) { - val checkpoint = Checkpoint.newBuilder() - .putBookNameToSessionAliasToDirectionCheckpoint( - "test_book", - Checkpoint.SessionAliasToDirectionCheckpoint.newBuilder() - .putSessionAliasToDirectionCheckpoint( - "test_alias", - Checkpoint.DirectionCheckpoint.newBuilder() - .putDirectionToCheckpointData( - 1, - Checkpoint.CheckpointData.newBuilder() - .setSequence(42) - .setTimestamp(Timestamps.now()) - .build(), - ) - .build(), - ) - .build(), - ) - .build() + val checkpoint = + Checkpoint + .newBuilder() + .putBookNameToSessionAliasToDirectionCheckpoint( + "test_book", + Checkpoint.SessionAliasToDirectionCheckpoint + .newBuilder() + .putSessionAliasToDirectionCheckpoint( + "test_alias", + Checkpoint.DirectionCheckpoint + .newBuilder() + .putDirectionToCheckpointData( + 1, + Checkpoint.CheckpointData + .newBuilder() + .setSequence(42) + .setTimestamp(Timestamps.now()) + .build(), + ).build(), + ).build(), + ).build() with(responseObserver) { onNext(CheckpointResponse.newBuilder().setCheckpoint(checkpoint).build()) onCompleted() diff --git a/src/test/kotlin/com/exactpro/th2/test/spec/TestRabbitMqSpec.kt b/src/test/kotlin/com/exactpro/th2/test/spec/TestRabbitMqSpec.kt index be57647..6e13de5 100644 --- a/src/test/kotlin/com/exactpro/th2/test/spec/TestRabbitMqSpec.kt +++ b/src/test/kotlin/com/exactpro/th2/test/spec/TestRabbitMqSpec.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Exactpro (Exactpro Systems Limited) + * Copyright 2023-2025 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,20 +38,22 @@ import java.time.Instant @Th2IntegrationTest internal class TestRabbitMqSpec { @JvmField - internal val spec = RabbitMqSpec.create() - .pins { - publishers { - pin("pin_name") { - attributes("a", "b", "transport-group") - filter { - message { - sessionAlias() shouldMatchWildcard "test_*" - messageType() shouldBeEqualTo "test" + internal val spec = + RabbitMqSpec + .create() + .pins { + publishers { + pin("pin_name") { + attributes("a", "b", "transport-group") + filter { + message { + sessionAlias() shouldMatchWildcard "test_*" + messageType() shouldBeEqualTo "test" + } } } } } - } @Test fun connectsEventPinBetweenAppAndTestFactories( @@ -81,30 +83,33 @@ internal class TestRabbitMqSpec { @Th2TestFactory testFactory: CommonFactory, ) { val appRouter = appFactory.transportGroupBatchRouter - val messageId: MessageId = MessageId.builder() - .setSessionAlias("test_1") - .setSequence(1) - .setTimestamp(Instant.now()) - .setDirection(Direction.OUTGOING) - .build() + val messageId: MessageId = + MessageId + .builder() + .setSessionAlias("test_1") + .setSequence(1) + .setTimestamp(Instant.now()) + .setDirection(Direction.OUTGOING) + .build() appRouter.send( - GroupBatch.builder() + GroupBatch + .builder() .setBook(BoxConfiguration.DEFAULT_BOOK_NAME) .setSessionGroup("session_group") .setGroups( listOf( - MessageGroup.builder() + MessageGroup + .builder() .addMessage( - ParsedMessage.builder() + ParsedMessage + .builder() .setType("test") .setId(messageId) .setBody(mapOf("test" to 42)) .build(), - ) - .build(), + ).build(), ), - ) - .build(), + ).build(), "a", ) val listener = createUnbound()