From e781d21097d4536c918b6d74387faf9c2648a6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Mon, 9 Nov 2020 10:09:26 +0100 Subject: [PATCH] Add CoroutinesAnnotationTransactionInterceptorTests suspendingValueSuccess() currently fails due to an unexpected rollback on transactional suspending functions returning a value. See gh-25998 --- ...esAnnotationTransactionInterceptorTests.kt | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 spring-tx/src/test/kotlin/org/springframework/transaction/annotation/CoroutinesAnnotationTransactionInterceptorTests.kt diff --git a/spring-tx/src/test/kotlin/org/springframework/transaction/annotation/CoroutinesAnnotationTransactionInterceptorTests.kt b/spring-tx/src/test/kotlin/org/springframework/transaction/annotation/CoroutinesAnnotationTransactionInterceptorTests.kt new file mode 100644 index 0000000000..7b38b7efe7 --- /dev/null +++ b/spring-tx/src/test/kotlin/org/springframework/transaction/annotation/CoroutinesAnnotationTransactionInterceptorTests.kt @@ -0,0 +1,117 @@ +package org.springframework.transaction.annotation + +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.springframework.aop.framework.ProxyFactory +import org.springframework.transaction.TransactionManager +import org.springframework.transaction.interceptor.TransactionInterceptor +import org.springframework.transaction.testfixture.CallCountingTransactionManager +import org.springframework.transaction.testfixture.ReactiveCallCountingTransactionManager + +class CoroutinesAnnotationTransactionInterceptorTests { + + private val ptm = CallCountingTransactionManager() + + private val rtm = ReactiveCallCountingTransactionManager() + + private val source = AnnotationTransactionAttributeSource() + + private val ti = TransactionInterceptor((ptm as TransactionManager), source) + + @Test + fun suspendingNoValueSuccess() { + val proxyFactory = ProxyFactory() + proxyFactory.setTarget(TestWithCoroutines()) + proxyFactory.addAdvice(TransactionInterceptor(rtm, source)) + val proxy = proxyFactory.proxy as TestWithCoroutines + runBlocking { + proxy.suspendingNoValueSuccess() + } + assertReactiveGetTransactionAndCommitCount(1) + } + + @Test + fun suspendingNoValueFailure() { + val proxyFactory = ProxyFactory() + proxyFactory.setTarget(TestWithCoroutines()) + proxyFactory.addAdvice(TransactionInterceptor(rtm, source)) + val proxy = proxyFactory.proxy as TestWithCoroutines + runBlocking { + try { + proxy.suspendingNoValueFailure() + } + catch (ex: IllegalStateException) { + } + + } + assertReactiveGetTransactionAndRollbackCount(1) + } + + @Test + @Disabled("Currently fails due to gh-25998") + fun suspendingValueSuccess() { + val proxyFactory = ProxyFactory() + proxyFactory.setTarget(TestWithCoroutines()) + proxyFactory.addAdvice(TransactionInterceptor(rtm, source)) + val proxy = proxyFactory.proxy as TestWithCoroutines + runBlocking { + Assertions.assertThat(proxy.suspendingValueSuccess()).isEqualTo("foo") + } + assertReactiveGetTransactionAndCommitCount(1) + } + + @Test + fun suspendingValueFailure() { + val proxyFactory = ProxyFactory() + proxyFactory.setTarget(TestWithCoroutines()) + proxyFactory.addAdvice(TransactionInterceptor(rtm, source)) + val proxy = proxyFactory.proxy as TestWithCoroutines + runBlocking { + try { + proxy.suspendingValueFailure() + } + catch (ex: IllegalStateException) { + } + + } + assertReactiveGetTransactionAndRollbackCount(1) + } + + + + private fun assertReactiveGetTransactionAndCommitCount(expectedCount: Int) { + Assertions.assertThat(rtm.begun).isEqualTo(expectedCount) + Assertions.assertThat(rtm.commits).isEqualTo(expectedCount) + } + + private fun assertReactiveGetTransactionAndRollbackCount(expectedCount: Int) { + Assertions.assertThat(rtm.begun).isEqualTo(expectedCount) + Assertions.assertThat(rtm.rollbacks).isEqualTo(expectedCount) + } + + @Transactional + open class TestWithCoroutines { + + open suspend fun suspendingNoValueSuccess() { + delay(10) + } + + open suspend fun suspendingNoValueFailure() { + delay(10) + throw IllegalStateException() + } + + open suspend fun suspendingValueSuccess(): String { + delay(10) + return "foo" + } + + open suspend fun suspendingValueFailure(): String { + delay(10) + throw IllegalStateException() + } + } +}