You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by sb...@apache.org on 2026/09/04 08:24:30 UTC
(grails-core) 01/01: test(grails-datamapping-core): restore the transaction service specs lost in the 7.2.x merge
This is an automated email from the ASF dual-hosted git repository.
sbglasius pushed a commit to branch test/restore-transaction-service-specs-8.0.x
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 857c98a98ca236f1803d9643473b6b83cfd8bdec
Author: Søren Berg Glasius <so...@glasius.dk>
AuthorDate: Fri Sep 4 10:17:58 2026 +0200
test(grails-datamapping-core): restore the transaction service specs lost in the 7.2.x merge
The 7.2.x -> 8.0.x merge hit an add/add conflict on
DefaultTransactionServiceSpec: both branches had independently created
it. The resolution kept 8.0.x's version, which had a single test, and
dropped 7.2.x's fourteen. 8.0.x went from 14 covered behaviours to 1.
The drop was accidental, not a scoping decision. The resolution was
half-applied: it took 7.2.x's four Spring transaction imports but not
the test methods that use them, leaving `PlatformTransactionManager`,
`TransactionDefinition`, `TransactionStatus` and
`TransactionSystemException` imported and unused on 8.0.x. Pre-merge
8.0.x had neither the imports nor the tests, so they arrived together
and only half survived.
Restore 7.2.x's suite and keep 8.0.x's accessor test, giving 15:
- the full `withTransaction`/`withRollback`/`withNewTransaction` matrix
across the Closure, TransactionDefinition and Map overloads, each with
its "datastore does not support transactions" counterpart
- `getDatastore/setDatastore round-trip`, covering the explicit
`datastore` field 8.0.x added to DefaultTransactionService
The two branches' DefaultTransactionService differ only by that added
field and its accessors - every method the restored tests exercise is
byte-identical between 7.2.x and 8.0.x, so the suite ports unchanged.
7.2.x's property-style `transactionService.datastore = ...` resolves to
the new setter.
Also drop the class-level comment that said the overloads were out of
scope. It is no longer true once the suite is restored, and it referred
to "this PR" and "item 14" of a plan that is not in the repository.
All 15 pass:
./gradlew :grails-datamapping-core:test --tests \
"org.grails.datastore.gorm.services.DefaultTransactionServiceSpec"
DefaultTenantServiceSpec, the other add/add in that merge, was resolved
cleanly and is left alone.
---
.../services/DefaultTransactionServiceSpec.groovy | 173 +++++++++++++++++++--
1 file changed, 160 insertions(+), 13 deletions(-)
diff --git a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/services/DefaultTransactionServiceSpec.groovy b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/services/DefaultTransactionServiceSpec.groovy
index 1a95b2e973..5b81777a2b 100644
--- a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/services/DefaultTransactionServiceSpec.groovy
+++ b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/services/DefaultTransactionServiceSpec.groovy
@@ -24,27 +24,174 @@ import org.springframework.transaction.TransactionStatus
import org.springframework.transaction.TransactionSystemException
import org.grails.datastore.mapping.core.Datastore
+import org.grails.datastore.mapping.transactions.CustomizableRollbackTransactionAttribute
+import org.grails.datastore.mapping.transactions.TransactionCapableDatastore
import spock.lang.Specification
-/**
- * The diff only adds the explicit {@code datastore}/{@code getDatastore()}/{@code setDatastore()}
- * {@code Service} contract (previously implicit/inherited) - the rest of this class (the
- * {@code withTransaction}/{@code withRollback}/{@code withNewTransaction} overloads) predates this
- * PR and is already exercised via real {@code TransactionService} usage in the separate
- * {@code grails-datamapping-core-test} module (which doesn't count toward this module's own
- * coverage - see item 14's cross-module attribution note), so out of scope here.
- */
class DefaultTransactionServiceSpec extends Specification {
- void "getDatastore/setDatastore round-trip"() {
+ DefaultTransactionService transactionService = new DefaultTransactionService()
+
+ PlatformTransactionManager transactionManager = Mock(PlatformTransactionManager) {
+ getTransaction(_) >> Mock(TransactionStatus)
+ }
+
+ TransactionCapableDatastore transactionCapableDatastore = Mock(TransactionCapableDatastore) {
+ getTransactionManager() >> transactionManager
+ }
+
+ void 'getDatastore/setDatastore round-trip'() {
+ given:
+ Datastore datastore = Mock(Datastore)
+
+ when:
+ transactionService.setDatastore(datastore)
+
+ then:
+ transactionService.getDatastore().is(datastore)
+ }
+
+ void 'withTransaction(Closure) executes the callable within a transaction'() {
+ given:
+ transactionService.datastore = transactionCapableDatastore
+
+ when:
+ String result = transactionService.withTransaction { status -> 'done' }
+
+ then:
+ result == 'done'
+ 1 * transactionManager.commit(_)
+ }
+
+ void 'withTransaction(Closure) throws when the datastore does not support transactions'() {
+ given:
+ transactionService.datastore = Mock(Datastore)
+
+ when:
+ transactionService.withTransaction { status -> 'done' }
+
+ then:
+ TransactionSystemException e = thrown(TransactionSystemException)
+ e.message.contains('does not support transactions')
+ }
+
+ void 'withRollback(Closure) executes the callable and rolls back'() {
+ given:
+ transactionService.datastore = transactionCapableDatastore
+
+ expect:
+ transactionService.withRollback { status -> 'done' } == 'done'
+ }
+
+ void 'withRollback(Closure) throws when the datastore does not support transactions'() {
+ given:
+ transactionService.datastore = Mock(Datastore)
+
+ when:
+ transactionService.withRollback { status -> 'done' }
+
+ then:
+ thrown(TransactionSystemException)
+ }
+
+ void 'withNewTransaction(Closure) executes the callable with PROPAGATION_REQUIRES_NEW'() {
+ given:
+ transactionService.datastore = transactionCapableDatastore
+
+ expect:
+ transactionService.withNewTransaction { status -> 'done' } == 'done'
+ }
+
+ void 'withNewTransaction(Closure) throws when the datastore does not support transactions'() {
+ given:
+ transactionService.datastore = Mock(Datastore)
+
+ when:
+ transactionService.withNewTransaction { status -> 'done' }
+
+ then:
+ thrown(TransactionSystemException)
+ }
+
+ void 'withTransaction(TransactionDefinition, Closure) executes the callable with the given definition'() {
+ given:
+ transactionService.datastore = transactionCapableDatastore
+ TransactionDefinition definition = new CustomizableRollbackTransactionAttribute()
+
+ expect:
+ transactionService.withTransaction(definition) { status -> 'done' } == 'done'
+ }
+
+ void 'withTransaction(TransactionDefinition, Closure) throws when the datastore does not support transactions'() {
+ given:
+ transactionService.datastore = Mock(Datastore)
+ TransactionDefinition definition = new CustomizableRollbackTransactionAttribute()
+
+ when:
+ transactionService.withTransaction(definition) { status -> 'done' }
+
+ then:
+ thrown(TransactionSystemException)
+ }
+
+ void 'withTransaction(Map, Closure) builds a transaction definition from the map'() {
+ given:
+ transactionService.datastore = transactionCapableDatastore
+
+ expect:
+ transactionService.withTransaction([readOnly: true]) { status -> 'done' } == 'done'
+ }
+
+ void 'withTransaction(Map, Closure) throws when the datastore does not support transactions'() {
+ given:
+ transactionService.datastore = Mock(Datastore)
+
+ when:
+ transactionService.withTransaction([readOnly: true]) { status -> 'done' }
+
+ then:
+ thrown(TransactionSystemException)
+ }
+
+ void 'withRollback(TransactionDefinition, Closure) executes the callable and rolls back'() {
+ given:
+ transactionService.datastore = transactionCapableDatastore
+ TransactionDefinition definition = new CustomizableRollbackTransactionAttribute()
+
+ expect:
+ transactionService.withRollback(definition) { status -> 'done' } == 'done'
+ }
+
+ void 'withRollback(TransactionDefinition, Closure) throws when the datastore does not support transactions'() {
+ given:
+ transactionService.datastore = Mock(Datastore)
+ TransactionDefinition definition = new CustomizableRollbackTransactionAttribute()
+
+ when:
+ transactionService.withRollback(definition) { status -> 'done' }
+
+ then:
+ thrown(TransactionSystemException)
+ }
+
+ void 'withNewTransaction(TransactionDefinition, Closure) forces PROPAGATION_REQUIRES_NEW'() {
+ given:
+ transactionService.datastore = transactionCapableDatastore
+ TransactionDefinition definition = new CustomizableRollbackTransactionAttribute()
+
+ expect:
+ transactionService.withNewTransaction(definition) { status -> 'done' } == 'done'
+ }
+
+ void 'withNewTransaction(TransactionDefinition, Closure) throws when the datastore does not support transactions'() {
given:
- def service = new DefaultTransactionService()
- def datastore = Mock(Datastore)
+ transactionService.datastore = Mock(Datastore)
+ TransactionDefinition definition = new CustomizableRollbackTransactionAttribute()
when:
- service.setDatastore(datastore)
+ transactionService.withNewTransaction(definition) { status -> 'done' }
then:
- service.getDatastore().is(datastore)
+ thrown(TransactionSystemException)
}
}