fixing(infra-messaging)

This commit is contained in:
2025-08-15 01:17:24 +02:00
parent ad91050a2f
commit 846918cf69
14 changed files with 2324 additions and 45 deletions
@@ -13,6 +13,8 @@ import org.springframework.kafka.support.serializer.JsonSerializer
*
* This class can be instantiated programmatically (as done in tests) or
* registered as a Spring @Configuration with @Bean methods in an application context.
*
* Enhanced with configuration validation and additional optimization settings.
*/
class KafkaConfig {
@@ -20,17 +22,52 @@ class KafkaConfig {
* Comma-separated list of host:port pairs used for establishing the initial connection to the Kafka cluster.
*/
var bootstrapServers: String = "localhost:9092"
set(value) {
require(value.isNotBlank()) { "Bootstrap servers cannot be blank" }
// Support both simple format (host:port) and protocol-prefixed format (PLAINTEXT://host:port)
val isValidFormat = value.matches(Regex("^[a-zA-Z0-9._-]+:[0-9]+(,[a-zA-Z0-9._-]+:[0-9]+)*$")) ||
value.matches(Regex("^[A-Z]+://[a-zA-Z0-9._-]+:[0-9]+(,[A-Z]+://[a-zA-Z0-9._-]+:[0-9]+)*$"))
require(isValidFormat) {
"Bootstrap servers must be in format 'host:port' or 'PROTOCOL://host:port'"
}
field = value
}
/**
* Default consumer group ID prefix.
*/
var defaultGroupIdPrefix: String = "messaging-client"
set(value) {
require(value.isNotBlank()) { "Default group ID prefix cannot be blank" }
require(value.matches(Regex("^[a-zA-Z0-9._-]+$"))) {
"Group ID prefix must contain only alphanumeric characters, dots, underscores, and hyphens"
}
field = value
}
/**
* Comma-separated list of trusted packages for JSON deserialization security.
* Default restricts to application packages only.
*/
var trustedPackages: String = "at.mocode.*"
set(value) {
require(value.isNotBlank()) { "Trusted packages cannot be blank" }
field = value
}
/**
* Enable additional security features for production environments.
*/
var enableSecurityFeatures: Boolean = true
/**
* Connection pool size for better resource management.
*/
var connectionPoolSize: Int = 10
set(value) {
require(value > 0) { "Connection pool size must be positive" }
field = value
}
/**
* Optimized producer properties with performance tuning and reliability settings.
@@ -0,0 +1,147 @@
package at.mocode.infrastructure.messaging.config
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.api.assertThrows
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class KafkaConfigTest {
@Test
fun `should validate bootstrap servers format`() {
val config = KafkaConfig()
// Valid formats
assertDoesNotThrow { config.bootstrapServers = "localhost:9092" }
assertDoesNotThrow { config.bootstrapServers = "PLAINTEXT://localhost:9092" }
assertDoesNotThrow { config.bootstrapServers = "host1:9092,host2:9092" }
assertDoesNotThrow { config.bootstrapServers = "PLAINTEXT://host1:9092,PLAINTEXT://host2:9092" }
assertDoesNotThrow { config.bootstrapServers = "kafka.example.com:9092" }
assertDoesNotThrow { config.bootstrapServers = "kafka-cluster-01.internal:9092" }
// Invalid formats
assertThrows<IllegalArgumentException> { config.bootstrapServers = "" }
assertThrows<IllegalArgumentException> { config.bootstrapServers = " " }
assertThrows<IllegalArgumentException> { config.bootstrapServers = "invalid-format" }
assertThrows<IllegalArgumentException> { config.bootstrapServers = "localhost" }
assertThrows<IllegalArgumentException> { config.bootstrapServers = ":9092" }
assertThrows<IllegalArgumentException> { config.bootstrapServers = "localhost:" }
assertThrows<IllegalArgumentException> { config.bootstrapServers = "localhost:abc" }
}
@Test
fun `should validate group ID prefix`() {
val config = KafkaConfig()
// Valid prefixes
assertDoesNotThrow { config.defaultGroupIdPrefix = "valid-prefix_123" }
assertDoesNotThrow { config.defaultGroupIdPrefix = "messaging-client" }
assertDoesNotThrow { config.defaultGroupIdPrefix = "test.group.id" }
assertDoesNotThrow { config.defaultGroupIdPrefix = "simple123" }
// Invalid prefixes
assertThrows<IllegalArgumentException> { config.defaultGroupIdPrefix = "" }
assertThrows<IllegalArgumentException> { config.defaultGroupIdPrefix = " " }
assertThrows<IllegalArgumentException> { config.defaultGroupIdPrefix = "invalid@prefix" }
assertThrows<IllegalArgumentException> { config.defaultGroupIdPrefix = "invalid#prefix" }
assertThrows<IllegalArgumentException> { config.defaultGroupIdPrefix = "invalid prefix" }
assertThrows<IllegalArgumentException> { config.defaultGroupIdPrefix = "invalid/prefix" }
}
@Test
fun `should validate trusted packages`() {
val config = KafkaConfig()
// Valid trusted packages
assertDoesNotThrow { config.trustedPackages = "at.mocode.*,com.example.*" }
assertDoesNotThrow { config.trustedPackages = "at.mocode.*" }
assertDoesNotThrow { config.trustedPackages = "com.example.specific.Package" }
assertDoesNotThrow { config.trustedPackages = "java.lang.*,java.util.*" }
// Invalid trusted packages
assertThrows<IllegalArgumentException> { config.trustedPackages = "" }
assertThrows<IllegalArgumentException> { config.trustedPackages = " " }
}
@Test
fun `should validate connection pool size`() {
val config = KafkaConfig()
// Valid pool sizes
assertDoesNotThrow { config.connectionPoolSize = 1 }
assertDoesNotThrow { config.connectionPoolSize = 5 }
assertDoesNotThrow { config.connectionPoolSize = 10 }
assertDoesNotThrow { config.connectionPoolSize = 100 }
// Invalid pool sizes
assertThrows<IllegalArgumentException> { config.connectionPoolSize = 0 }
assertThrows<IllegalArgumentException> { config.connectionPoolSize = -1 }
assertThrows<IllegalArgumentException> { config.connectionPoolSize = -10 }
}
@Test
fun `should have default values set correctly`() {
val config = KafkaConfig()
assertThat(config.bootstrapServers).isEqualTo("localhost:9092")
assertThat(config.defaultGroupIdPrefix).isEqualTo("messaging-client")
assertThat(config.trustedPackages).isEqualTo("at.mocode.*")
assertThat(config.enableSecurityFeatures).isEqualTo(true)
assertThat(config.connectionPoolSize).isEqualTo(10)
}
@Test
fun `should generate valid producer configs`() {
val config = KafkaConfig()
val producerConfigs = config.producerConfigs()
// Verify essential producer configuration
assertThat(producerConfigs["bootstrap.servers"]).isEqualTo("localhost:9092")
assertThat(producerConfigs["key.serializer"]).isEqualTo(org.apache.kafka.common.serialization.StringSerializer::class.java)
assertThat(producerConfigs["value.serializer"]).isEqualTo(org.springframework.kafka.support.serializer.JsonSerializer::class.java)
assertThat(producerConfigs["acks"]).isEqualTo("all")
assertThat(producerConfigs["enable.idempotence"]).isEqualTo(true)
}
@Test
fun `should generate valid consumer configs with custom group ID`() {
val config = KafkaConfig()
val customGroupId = "test-group-123"
val consumerConfigs = config.consumerConfigs(customGroupId)
// Verify essential consumer configuration
assertThat(consumerConfigs["bootstrap.servers"]).isEqualTo("localhost:9092")
assertThat(consumerConfigs["group.id"]).isEqualTo(customGroupId)
assertThat(consumerConfigs["key.deserializer"]).isEqualTo(org.apache.kafka.common.serialization.StringDeserializer::class.java)
assertThat(consumerConfigs["value.deserializer"]).isEqualTo(org.springframework.kafka.support.serializer.JsonDeserializer::class.java)
assertThat(consumerConfigs["spring.json.trusted.packages"]).isEqualTo("at.mocode.*")
assertThat(consumerConfigs["auto.offset.reset"]).isEqualTo("earliest")
assertThat(consumerConfigs["enable.auto.commit"]).isEqualTo(false)
}
@Test
fun `should generate unique consumer configs when no group ID provided`() {
val config = KafkaConfig()
val consumerConfigs1 = config.consumerConfigs()
val consumerConfigs2 = config.consumerConfigs()
// Group IDs should be different (timestamp-based)
val groupId1 = consumerConfigs1["group.id"].toString()
val groupId2 = consumerConfigs2["group.id"].toString()
assertThat(groupId1).isNotEqualTo(groupId2)
assertThat(groupId1).startsWith("messaging-client-")
assertThat(groupId2).startsWith("messaging-client-")
}
@Test
fun `should create producer factory with correct configuration`() {
val config = KafkaConfig()
val producerFactory = config.producerFactory()
assertDoesNotThrow { producerFactory.createProducer() }
assertThat(producerFactory.configurationProperties["bootstrap.servers"]).isEqualTo("localhost:9092")
}
}