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

jamesfredley pushed a commit to branch fix/flaky-ersatz-listener-test
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit f59f9371af0725842673e0ff177c60c82e5c92e9
Author: James Fredley <[email protected]>
AuthorDate: Wed Feb 25 12:29:25 2026 -0500

    fix: flaky ersatz listener test due to async race condition
    
    The 'ersatz listener captures request details' test in
    MicronautErsatzAdvancedSpec intermittently fails because the Ersatz
    listener callback executes asynchronously on a server thread while the
    test assertion runs immediately on the test thread. Use a thread-safe
    CopyOnWriteArrayList and Spock PollingConditions to wait for the
    listener to populate before asserting.
    
    Assisted-by: OpenCode <[email protected]>
---
 .../groovy/micronaut/MicronautErsatzAdvancedSpec.groovy       | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git 
a/grails-test-examples/micronaut/src/integration-test/groovy/micronaut/MicronautErsatzAdvancedSpec.groovy
 
b/grails-test-examples/micronaut/src/integration-test/groovy/micronaut/MicronautErsatzAdvancedSpec.groovy
index d5f036c152..21c1c760cc 100644
--- 
a/grails-test-examples/micronaut/src/integration-test/groovy/micronaut/MicronautErsatzAdvancedSpec.groovy
+++ 
b/grails-test-examples/micronaut/src/integration-test/groovy/micronaut/MicronautErsatzAdvancedSpec.groovy
@@ -31,10 +31,13 @@ import micronaut.client.MicronautHeaderClient
 import micronaut.client.MicronautTestClient
 import spock.lang.AutoCleanup
 import spock.lang.Specification
+import spock.util.concurrent.PollingConditions
 
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.beans.factory.annotation.Value
 
+import java.util.concurrent.CopyOnWriteArrayList
+
 import grails.testing.mixin.integration.Integration
 
 @Integration
@@ -556,7 +559,7 @@ class MicronautErsatzAdvancedSpec extends Specification {
 
     void "ersatz listener captures request details"() {
         given: 'ersatz captures request details'
-        List<Object> captured = []
+        CopyOnWriteArrayList<Object> captured = new CopyOnWriteArrayList<>()
         ersatz.expectations({ expect ->
             expect.GET('/micronaut-test/listen', { req ->
                 req.listener({ captured << it })
@@ -579,7 +582,11 @@ class MicronautErsatzAdvancedSpec extends Specification {
 
         then: 'a request was captured'
         response.status.code == 200
-        captured.size() == 1
+
+        and: 'the listener eventually captures the request'
+        new PollingConditions(timeout: 5).eventually {
+            assert captured.size() == 1
+        }
 
         and: 'ersatz verifies the call'
         ersatz.verify()

Reply via email to