This is an automated email from the ASF dual-hosted git repository.

jdaugherty pushed a commit to branch database-cleanup-feature
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 1a91738cdf34006589d55c60789494bac9e2d799
Author: Test <[email protected]>
AuthorDate: Sat Feb 21 23:20:37 2026 -0500

    Ensure cleanup runs even if the clean methods fail on the spec
---
 .../cleanup/core/DatabaseCleanupInterceptor.groovy | 50 +++++++-------
 .../core/DatabaseCleanupInterceptorSpec.groovy     | 76 ++++++++++++++++++++++
 2 files changed, 104 insertions(+), 22 deletions(-)

diff --git 
a/grails-testing-support-cleanup-core/src/main/groovy/org/apache/grails/testing/cleanup/core/DatabaseCleanupInterceptor.groovy
 
b/grails-testing-support-cleanup-core/src/main/groovy/org/apache/grails/testing/cleanup/core/DatabaseCleanupInterceptor.groovy
index 1e0127a207..e9616913e5 100644
--- 
a/grails-testing-support-cleanup-core/src/main/groovy/org/apache/grails/testing/cleanup/core/DatabaseCleanupInterceptor.groovy
+++ 
b/grails-testing-support-cleanup-core/src/main/groovy/org/apache/grails/testing/cleanup/core/DatabaseCleanupInterceptor.groovy
@@ -62,34 +62,40 @@ class DatabaseCleanupInterceptor extends 
AbstractMethodInterceptor {
 
     @Override
     void interceptCleanupMethod(IMethodInvocation invocation) throws Throwable 
{
-        invocation.proceed()
-
-        if (classLevelCleanup) {
-            ensureApplicationContext(invocation)
-            log.debug('Performing database cleanup after test method: {}',
-                    invocation.feature?.name ?: 'unknown')
-            List<DatabaseCleanupStats> stats = context.performCleanup(mapping)
-            logStats(stats)
+        try {
+            invocation.proceed()
         }
-        else if (isCurrentFeatureAnnotated(invocation)) {
-            ensureApplicationContext(invocation)
-            DatasourceCleanupMapping methodMapping = 
getMethodMapping(invocation)
-            log.debug('Performing database cleanup after test method: {}',
-                    invocation.feature?.name ?: 'unknown')
-            List<DatabaseCleanupStats> stats = 
context.performCleanup(methodMapping)
-            logStats(stats)
+        finally {
+            if (classLevelCleanup) {
+                ensureApplicationContext(invocation)
+                log.debug('Performing database cleanup after test method: {}',
+                        invocation.feature?.name ?: 'unknown')
+                List<DatabaseCleanupStats> stats = 
context.performCleanup(mapping)
+                logStats(stats)
+            }
+            else if (isCurrentFeatureAnnotated(invocation)) {
+                ensureApplicationContext(invocation)
+                DatasourceCleanupMapping methodMapping = 
getMethodMapping(invocation)
+                log.debug('Performing database cleanup after test method: {}',
+                        invocation.feature?.name ?: 'unknown')
+                List<DatabaseCleanupStats> stats = 
context.performCleanup(methodMapping)
+                logStats(stats)
+            }
         }
     }
 
     @Override
     void interceptCleanupSpecMethod(IMethodInvocation invocation) throws 
Throwable {
-        invocation.proceed()
-
-        if (classLevelCleanup) {
-            ensureApplicationContext(invocation)
-            log.debug('Performing database cleanup after spec: {}', 
invocation.spec?.name ?: 'unknown')
-            List<DatabaseCleanupStats> stats = context.performCleanup(mapping)
-            logStats(stats)
+        try {
+            invocation.proceed()
+        }
+        finally {
+            if (classLevelCleanup) {
+                ensureApplicationContext(invocation)
+                log.debug('Performing database cleanup after spec: {}', 
invocation.spec?.name ?: 'unknown')
+                List<DatabaseCleanupStats> stats = 
context.performCleanup(mapping)
+                logStats(stats)
+            }
         }
     }
 
diff --git 
a/grails-testing-support-cleanup-core/src/test/groovy/org/apache/grails/testing/cleanup/core/DatabaseCleanupInterceptorSpec.groovy
 
b/grails-testing-support-cleanup-core/src/test/groovy/org/apache/grails/testing/cleanup/core/DatabaseCleanupInterceptorSpec.groovy
index 9cfcb82adf..6341e81c17 100644
--- 
a/grails-testing-support-cleanup-core/src/test/groovy/org/apache/grails/testing/cleanup/core/DatabaseCleanupInterceptorSpec.groovy
+++ 
b/grails-testing-support-cleanup-core/src/test/groovy/org/apache/grails/testing/cleanup/core/DatabaseCleanupInterceptorSpec.groovy
@@ -447,6 +447,82 @@ class DatabaseCleanupInterceptorSpec extends Specification 
{
         System.out = originalOut
     }
 
+    def "cleanup still runs even if test method fails"() {
+        given:
+        def dataSource = Mock(DataSource)
+        def appCtx = Mock(ApplicationContext) {
+            getBeansOfType(DataSource) >> ['dataSource': dataSource]
+        }
+        def cleaner = Mock(DatabaseCleaner) {
+            databaseType() >> 'h2'
+            supports(dataSource) >> true
+            cleanup(appCtx, dataSource) >> new DatabaseCleanupStats()
+        }
+        def context = new DatabaseCleanupContext([cleaner])
+        context.applicationContext = appCtx
+
+        def mapping = DatasourceCleanupMapping.parse(new String[0])
+        def resolver = new DefaultApplicationContextResolver()
+        def interceptor = new DatabaseCleanupInterceptor(context, true, 
mapping, resolver)
+
+        def testFailure = new RuntimeException('Test failed')
+        def invocation = Mock(IMethodInvocation) {
+            getInstance() >> new InstanceWithAppCtx(applicationContext: appCtx)
+            getFeature() >> Mock(FeatureInfo) {
+                getName() >> 'test feature'
+            }
+            proceed() >> { throw testFailure }
+        }
+
+        when:
+        interceptor.interceptCleanupMethod(invocation)
+
+        then: 'cleanup was performed before exception is re-thrown'
+        1 * cleaner.cleanup(appCtx, dataSource) >> new DatabaseCleanupStats()
+
+        and: 'the original test exception is re-thrown'
+        RuntimeException ex = thrown(RuntimeException)
+        ex.is(testFailure)
+    }
+
+    def "cleanup still runs even if cleanupSpec method fails"() {
+        given:
+        def dataSource = Mock(DataSource)
+        def appCtx = Mock(ApplicationContext) {
+            getBeansOfType(DataSource) >> ['dataSource': dataSource]
+        }
+        def cleaner = Mock(DatabaseCleaner) {
+            databaseType() >> 'h2'
+            supports(dataSource) >> true
+            cleanup(appCtx, dataSource) >> new DatabaseCleanupStats()
+        }
+        def context = new DatabaseCleanupContext([cleaner])
+        context.applicationContext = appCtx
+
+        def mapping = DatasourceCleanupMapping.parse(new String[0])
+        def resolver = new DefaultApplicationContextResolver()
+        def interceptor = new DatabaseCleanupInterceptor(context, true, 
mapping, resolver)
+
+        def specFailure = new RuntimeException('Cleanup spec failed')
+        def invocation = Mock(IMethodInvocation) {
+            getInstance() >> new InstanceWithAppCtx(applicationContext: appCtx)
+            getSpec() >> Mock(SpecInfo) {
+                getName() >> 'TestSpec'
+            }
+            proceed() >> { throw specFailure }
+        }
+
+        when:
+        interceptor.interceptCleanupSpecMethod(invocation)
+
+        then: 'cleanup was performed before exception is re-thrown'
+        1 * cleaner.cleanup(appCtx, dataSource) >> new DatabaseCleanupStats()
+
+        and: 'the original spec exception is re-thrown'
+        RuntimeException ex = thrown(RuntimeException)
+        ex.is(specFailure)
+    }
+
     // --- Helper classes ---
 
     static class InstanceWithAppCtx {

Reply via email to