fixing(gradle)

This commit is contained in:
2025-08-24 21:31:31 +02:00
parent 8d01fa0e9a
commit 89ef9698af
77 changed files with 2060 additions and 1656 deletions
@@ -35,11 +35,21 @@ interface EventConsumer {
fun <T : Any> receiveEvents(topic: String, eventType: Class<T>): Flux<T>
}
/**
* Kotlin-idiomatic extension function for `receiveEventsWithResult` using reified types.
*
* Example: `consumer.receiveEventsWithResult<MyEvent>("my-topic").collect { result -> ... }`
*/
inline fun <reified T : Any> EventConsumer.receiveEventsWithResult(topic: String): Flow<Result<T>> {
return this.receiveEventsWithResult(topic, T::class.java)
}
/**
* Kotlin-idiomatic extension function for `receiveEvents` using reified types.
*
* Example: `consumer.receiveEvents<MyEvent>("my-topic").subscribe { ... }`
*/
@Deprecated("Use receiveEventsWithResult with Flow<Result<T>> instead", ReplaceWith("receiveEventsWithResult<T>(topic)"))
inline fun <reified T : Any> EventConsumer.receiveEvents(topic: String): Flux<T> {
return this.receiveEvents(topic, T::class.java)
}
@@ -120,8 +120,8 @@ class KafkaEventConsumerCacheTest {
assertThat(secureConsumer).isNotNull
// Should be able to create streams
val flux = secureConsumer.receiveEvents<TestEvent>("secure-topic")
assertThat(flux).isNotNull
val flow = secureConsumer.receiveEventsWithResult<TestEvent>("secure-topic")
assertThat(flow).isNotNull
}
}
@@ -143,8 +143,8 @@ class KafkaEventConsumerCacheTest {
assertDoesNotThrow {
val testConsumer = KafkaEventConsumer(config)
val flux = testConsumer.receiveEvents<TestEvent>("validation-topic")
assertThat(flux).isNotNull
val flow = testConsumer.receiveEventsWithResult<TestEvent>("validation-topic")
assertThat(flow).isNotNull
}
}
}
@@ -165,8 +165,8 @@ class KafkaEventConsumerCacheTest {
assertThat(testConsumer).isNotNull
// Should be able to create reactive streams
val flux = testConsumer.receiveEvents<TestEvent>("pool-test-topic")
assertThat(flux).isNotNull
val flow = testConsumer.receiveEventsWithResult<TestEvent>("pool-test-topic")
assertThat(flow).isNotNull
}
}
}
@@ -189,22 +189,22 @@ class KafkaEventConsumerCacheTest {
assertDoesNotThrow {
val testConsumer = KafkaEventConsumer(config)
val flux = testConsumer.receiveEvents<TestEvent>("prefix-test-topic")
assertThat(flux).isNotNull
val flow = testConsumer.receiveEventsWithResult<TestEvent>("prefix-test-topic")
assertThat(flow).isNotNull
}
}
}
@Test
fun `should support extension function for reified types`() {
// Test the Kotlin extension function receiveEvents<T>()
// Test the Kotlin extension function receiveEventsWithResult<T>()
assertDoesNotThrow {
val fluxWithReified = consumer.receiveEvents<TestEvent>("reified-topic")
val fluxWithClass = consumer.receiveEvents("reified-topic", TestEvent::class.java)
val flowWithReified = consumer.receiveEventsWithResult<TestEvent>("reified-topic")
val flowWithClass = consumer.receiveEventsWithResult("reified-topic", TestEvent::class.java)
// Both should work and create valid Flux instances
assertThat(fluxWithReified).isNotNull
assertThat(fluxWithClass).isNotNull
// Both should work and create valid Flow instances
assertThat(flowWithReified).isNotNull
assertThat(flowWithClass).isNotNull
}
}
@@ -226,8 +226,8 @@ class KafkaEventConsumerCacheTest {
assertThat(testConsumer).isNotNull
// Each should be able to create streams
val flux = testConsumer.receiveEvents<TestEvent>("concurrent-topic")
assertThat(flux).isNotNull
val flow = testConsumer.receiveEventsWithResult<TestEvent>("concurrent-topic")
assertThat(flow).isNotNull
}
// Clean up all consumers
@@ -3,13 +3,13 @@ package at.mocode.infrastructure.messaging.client
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.springframework.kafka.core.reactive.ReactiveKafkaProducerTemplate
import reactor.core.publisher.Mono
import reactor.kafka.sender.SenderResult
import reactor.test.StepVerifier
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class KafkaEventPublisherErrorTest {
@@ -24,7 +24,7 @@ class KafkaEventPublisherErrorTest {
}
@Test
fun `should publish single event successfully`() {
fun `should publish single event successfully`() = runTest {
val testEvent = TestEvent("data")
val mockResult = mockk<SenderResult<Void>>()
val mockRecordMetadata = mockk<org.apache.kafka.clients.producer.RecordMetadata>()
@@ -35,51 +35,55 @@ class KafkaEventPublisherErrorTest {
every { mockTemplate.send("test-topic", "key", testEvent) } returns Mono.just(mockResult)
StepVerifier.create(publisher.publishEventReactive("test-topic", "key", testEvent))
.expectNext(Unit)
.verifyComplete()
val result = publisher.publishEvent("test-topic", "key", testEvent)
assert(result.isSuccess) { "Expected successful result" }
verify(exactly = 1) { mockTemplate.send("test-topic", "key", testEvent) }
}
@Test
fun `should handle serialization errors without retry`() {
fun `should handle serialization errors without retry`() = runTest {
val testEvent = TestEvent("data")
every { mockTemplate.send("test-topic", "key", testEvent) } returns
Mono.error(RuntimeException("Serialization failed"))
StepVerifier.create(publisher.publishEventReactive("test-topic", "key", testEvent))
.verifyError(RuntimeException::class.java)
val result = publisher.publishEvent("test-topic", "key", testEvent)
assert(result.isFailure) { "Expected failed result" }
assert(result.exceptionOrNull() is MessagingError.SerializationError) { "Expected MessagingError.SerializationError" }
assert(result.exceptionOrNull()?.message?.contains("Serialization failed") == true) { "Expected specific error message" }
verify(exactly = 1) { mockTemplate.send("test-topic", "key", testEvent) }
}
@Test
fun `should handle authentication errors without retry`() {
fun `should handle authentication errors without retry`() = runTest {
val testEvent = TestEvent("data")
every { mockTemplate.send("test-topic", "key", testEvent) } returns
Mono.error(RuntimeException("Authentication failed"))
StepVerifier.create(publisher.publishEventReactive("test-topic", "key", testEvent))
.verifyError(RuntimeException::class.java)
val result = publisher.publishEvent("test-topic", "key", testEvent)
assert(result.isFailure) { "Expected failed result" }
assert(result.exceptionOrNull() is MessagingError.AuthenticationError) { "Expected MessagingError.AuthenticationError" }
assert(result.exceptionOrNull()?.message?.contains("Authentication failed") == true) { "Expected specific error message" }
verify(exactly = 1) { mockTemplate.send("test-topic", "key", testEvent) }
}
@Test
fun `should handle empty batch gracefully`() {
fun `should handle empty batch gracefully`() = runTest {
val emptyEvents = emptyList<Pair<String?, Any>>()
StepVerifier.create(publisher.publishEventsReactive("test-topic", emptyEvents))
.verifyComplete()
val result = publisher.publishEvents("test-topic", emptyEvents)
assert(result.isSuccess) { "Expected successful result for empty batch" }
assert(result.getOrNull()?.isEmpty() == true) { "Expected empty result list" }
verify(exactly = 0) { mockTemplate.send(any(), any(), any()) }
}
@Test
fun `should publish batch events successfully`() {
fun `should publish batch events successfully`() = runTest {
val events = listOf(
"key1" to TestEvent("message1"),
"key2" to TestEvent("message2")
@@ -95,10 +99,10 @@ class KafkaEventPublisherErrorTest {
every { mockTemplate.send("test-topic", "key1", any()) } returns Mono.just(mockResult)
every { mockTemplate.send("test-topic", "key2", any()) } returns Mono.just(mockResult)
StepVerifier.create(publisher.publishEventsReactive("test-topic", events))
.expectNextCount(2)
.verifyComplete()
val result = publisher.publishEvents("test-topic", events)
assert(result.isSuccess) { "Expected successful batch result" }
assert(result.getOrNull()?.size == 2) { "Expected 2 successful operations" }
verify(exactly = 1) { mockTemplate.send("test-topic", "key1", any()) }
verify(exactly = 1) { mockTemplate.send("test-topic", "key2", any()) }
}
@@ -1,6 +1,7 @@
package at.mocode.infrastructure.messaging.client
import at.mocode.infrastructure.messaging.config.KafkaConfig
import kotlinx.coroutines.test.runTest
import org.apache.kafka.common.serialization.StringDeserializer
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
@@ -46,7 +47,7 @@ class KafkaIntegrationTest {
}
@Test
fun `publishEvent should send a message that can be received`() {
fun `publishEvent should send a message that can be received`() = runTest {
// Arrange
val testKey = "test-key"
val testEvent = TestEvent("Test Message")
@@ -75,19 +76,18 @@ class KafkaIntegrationTest {
.next() // Take only the first event
.map { it.value() } // Extract the value (our TestEvent instance)
// The Mono that represents the send action
val sendAction = kafkaEventPublisher.publishEventReactive(testTopic, testKey, testEvent)
// Execute the send action and verify success
val publishResult = kafkaEventPublisher.publishEvent(testTopic, testKey, testEvent)
assert(publishResult.isSuccess) { "Expected successful publish result" }
// CORRECTION: Combine the send action and receive expectation in one StepVerifier.
// The `then` method ensures that the send action is completed first,
// before the `receivedEvent` Mono is subscribed and verified.
StepVerifier.create(sendAction.then(receivedEvent))
// Verify that the message can be received
StepVerifier.create(receivedEvent)
.expectNext(testEvent) // Expect that our test event arrives
.verifyComplete() // Complete the verification
}
@Test
fun `publishEvents should send batch messages that can be received`() {
fun `publishEvents should send batch messages that can be received`() = runTest {
// Arrange
val batchSize = 10
val eventBatch = (1..batchSize).map { i ->
@@ -117,10 +117,13 @@ class KafkaIntegrationTest {
.map { it.value() }
.collectList()
// Send batch and verify reception
val sendAction = kafkaEventPublisher.publishEventsReactive(testTopic, eventBatch)
// Send batch and verify success
val publishResult = kafkaEventPublisher.publishEvents(testTopic, eventBatch)
assert(publishResult.isSuccess) { "Expected successful batch publish result" }
assert(publishResult.getOrNull()?.size == batchSize) { "Expected $batchSize successful operations" }
StepVerifier.create(sendAction.then(receivedEvents))
// Verify reception
StepVerifier.create(receivedEvents)
.expectNextMatches { events ->
events.size == batchSize && events.all { it.message.startsWith("Batch message") }
}
@@ -128,7 +131,7 @@ class KafkaIntegrationTest {
}
@Test
fun `should handle multiple consumers on same topic`() {
fun `should handle multiple consumers on same topic`() = runTest {
val testEvent = TestEvent("Multi-consumer message")
val testKey = "multi-consumer-key"
@@ -170,10 +173,12 @@ class KafkaIntegrationTest {
.next()
.map { it.value() }
val sendAction = kafkaEventPublisher.publishEventReactive(testTopic, testKey, testEvent)
// Execute the send action and verify success
val publishResult = kafkaEventPublisher.publishEvent(testTopic, testKey, testEvent)
assert(publishResult.isSuccess) { "Expected successful publish result" }
// Both consumers should receive the same message (different groups)
StepVerifier.create(sendAction.then(consumer1Event.zipWith(consumer2Event)))
StepVerifier.create(consumer1Event.zipWith(consumer2Event))
.expectNextMatches { tuple ->
tuple.t1 == testEvent && tuple.t2 == testEvent
}
@@ -181,7 +186,7 @@ class KafkaIntegrationTest {
}
@Test
fun `should handle different event types in integration scenario`() {
fun `should handle different event types in integration scenario`() = runTest {
val complexEvent = ComplexTestEvent(
id = 123,
name = "Integration Test",
@@ -209,15 +214,18 @@ class KafkaIntegrationTest {
.next()
.map { it.value() }
val sendAction = kafkaEventPublisher.publishEventReactive(testTopic, "complex-key", complexEvent)
// Execute the send action and verify success
val publishResult = kafkaEventPublisher.publishEvent(testTopic, "complex-key", complexEvent)
assert(publishResult.isSuccess) { "Expected successful publish result" }
StepVerifier.create(sendAction.then(receivedEvent))
// Verify that the complex event can be received
StepVerifier.create(receivedEvent)
.expectNext(complexEvent)
.verifyComplete()
}
@Test
fun `should maintain message ordering within partition`() {
fun `should maintain message ordering within partition`() = runTest {
val partitionKey = "ordered-messages"
val messageCount = 5
val orderedEvents = (1..messageCount).map { i ->
@@ -245,9 +253,13 @@ class KafkaIntegrationTest {
.map { it.value() }
.collectList()
val sendAction = kafkaEventPublisher.publishEventsReactive(testTopic, orderedEvents)
// Send ordered events and verify success
val publishResult = kafkaEventPublisher.publishEvents(testTopic, orderedEvents)
assert(publishResult.isSuccess) { "Expected successful batch publish result" }
assert(publishResult.getOrNull()?.size == messageCount) { "Expected $messageCount successful operations" }
StepVerifier.create(sendAction.then(receivedEvents))
// Verify message ordering is maintained
StepVerifier.create(receivedEvents)
.expectNextMatches { events ->
events.size == messageCount &&
events.mapIndexed { index, event ->
@@ -258,11 +270,12 @@ class KafkaIntegrationTest {
}
@Test
fun `should handle empty batch gracefully in integration test`() {
fun `should handle empty batch gracefully in integration test`() = runTest {
val emptyBatch = emptyList<Pair<String?, Any>>()
StepVerifier.create(kafkaEventPublisher.publishEventsReactive(testTopic, emptyBatch))
.verifyComplete()
val publishResult = kafkaEventPublisher.publishEvents(testTopic, emptyBatch)
assert(publishResult.isSuccess) { "Expected successful result for empty batch" }
assert(publishResult.getOrNull()?.isEmpty() == true) { "Expected empty result list" }
}
data class TestEvent(val message: String)