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

jhelou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 1705d89822ac5f7f8abbca187bcf87cbae9b008a
Author: Jean Helou <j...@xn--gml-cma.com>
AuthorDate: Fri Sep 20 09:04:19 2024 +0200

    [JAMES-3696] improves flaky tests from ManageableMailQueueContract
    
    The pulsar implementation uses a distributed and therefore asynchronous 
distribution of the removal filters.
    
    This change ensures that the tests allow for such an implementation by 
waiting for the system under test to converge to the desired state.
    
    It also ensures that the executor waits as little as possible to get into a 
consistent state, ensuring fast local execution while allowing a longer wait on 
the CI. If the default timeout (10s if I understand correctly) is not enough 
for the CI, it can be safely increased without slowing down local test 
execution like Thread.sleep() did.
---
 .../api/DelayedManageableMailQueueContract.java    |  10 +-
 .../queue/api/ManageableMailQueueContract.java     | 113 ++++++++++++---------
 .../james/queue/pulsar/PulsarMailQueueTest.java    |  26 ++---
 3 files changed, 75 insertions(+), 74 deletions(-)

diff --git 
a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java
 
b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java
index 442c42af22..782b207899 100644
--- 
a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java
+++ 
b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/DelayedManageableMailQueueContract.java
@@ -123,11 +123,11 @@ public interface DelayedManageableMailQueueContract 
extends DelayedMailQueueCont
 
         getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, 
RECIPIENT2.asString());
 
-        awaitRemove();
-
-        assertThat(getManageableMailQueue().browse()).toIterable()
-                .extracting(mail -> mail.getMail().getName())
-                .containsExactly("name2");
+        Awaitility.await().untilAsserted(() ->
+                assertThat(getManageableMailQueue().browse()).toIterable()
+                        .extracting(mail -> mail.getMail().getName())
+                        .containsExactly("name2")
+        );
     }
 
     @Test
diff --git 
a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java
 
b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java
index cae74f4d77..c174fe6bc5 100644
--- 
a/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java
+++ 
b/server/queue/queue-api/src/test/java/org/apache/james/queue/api/ManageableMailQueueContract.java
@@ -34,6 +34,7 @@ import java.time.Duration;
 import java.util.Iterator;
 import java.util.List;
 import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 import java.util.stream.Stream;
 
 import jakarta.mail.MessagingException;
@@ -42,12 +43,10 @@ import jakarta.mail.internet.MimeMessage;
 
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.builder.MimeMessageBuilder;
-import org.apache.james.junit.categories.Unstable;
 import org.apache.mailet.Attribute;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.MailAddressFixture;
 import org.awaitility.Awaitility;
-import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -62,10 +61,6 @@ import reactor.core.publisher.Mono;
 
 public interface ManageableMailQueueContract extends MailQueueContract {
 
-    default void awaitRemove() {
-
-    }
-
     ManageableMailQueue getManageableMailQueue();
 
     @Test
@@ -459,24 +454,27 @@ public interface ManageableMailQueueContract extends 
MailQueueContract {
 
     @Test
     default void browseShouldNotFailWhenConcurrentRemoveWhenIterating() throws 
Exception {
-        enQueue(defaultMail()
-            .name("name1")
-            .build());
-        enQueue(defaultMail()
-            .name("name2")
-            .build());
-        enQueue(defaultMail()
-            .name("name3")
-            .build());
+        // We use a large number of emails so that the probability of the 
remove being propagated
+        // through the queue is high enough that the test is not flaky.
+        IntStream.range(0, 100).forEach(
+                Throwing.intConsumer(i ->
+                        enQueue(defaultMail()
+                                .name("name" + i)
+                                .build())
+                ).sneakyThrow()
+        );
 
         ManageableMailQueue.MailQueueIterator items = 
getManageableMailQueue().browse();
-        items.next();
+        items.next();// we consume 1 here
 
-        getManageableMailQueue().remove(ManageableMailQueue.Type.Name, 
"name2");
+        getManageableMailQueue().remove(ManageableMailQueue.Type.Name, 
"name98"); // we remove 1 here
 
-        awaitRemove();
 
-        assertThatCode(() ->  
consumeIterator(items)).doesNotThrowAnyException();
+        assertThatCode(
+                // the remove may or may not be applied to an already started 
iterator
+                // as long as it doesn't make the iterator crash
+                () -> 
assertThat(consumeIterator(items)).isGreaterThanOrEqualTo(98).isLessThan(100)
+        ).doesNotThrowAnyException();
     }
 
     @Test
@@ -551,13 +549,13 @@ public interface ManageableMailQueueContract extends 
MailQueueContract {
 
         getManageableMailQueue().remove(ManageableMailQueue.Type.Name, 
"name2");
 
-        awaitRemove();
-
-        assertThat(getManageableMailQueue().browse())
-            .toIterable()
-            .extracting(ManageableMailQueue.MailQueueItemView::getMail)
-            .extracting(Mail::getName)
-            .containsExactly("name1");
+        Awaitility.await().untilAsserted(() ->
+                assertThat(getManageableMailQueue().browse())
+                        .toIterable()
+                        
.extracting(ManageableMailQueue.MailQueueItemView::getMail)
+                        .extracting(Mail::getName)
+                        .containsExactly("name1")
+        );
     }
 
     @Test
@@ -573,13 +571,13 @@ public interface ManageableMailQueueContract extends 
MailQueueContract {
 
         getManageableMailQueue().remove(ManageableMailQueue.Type.Sender, 
OTHER_AT_LOCAL.asString());
 
-        awaitRemove();
-
-        assertThat(getManageableMailQueue().browse())
-            .toIterable()
-            .extracting(ManageableMailQueue.MailQueueItemView::getMail)
-            .extracting(Mail::getName)
-            .containsExactly("name2");
+        Awaitility.await().untilAsserted(() ->
+                assertThat(getManageableMailQueue().browse())
+                        .toIterable()
+                        
.extracting(ManageableMailQueue.MailQueueItemView::getMail)
+                        .extracting(Mail::getName)
+                        .containsExactly("name2")
+        );
     }
 
     @Test
@@ -595,13 +593,13 @@ public interface ManageableMailQueueContract extends 
MailQueueContract {
 
         getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, 
RECIPIENT2.asString());
 
-        awaitRemove();
-
-        assertThat(getManageableMailQueue().browse())
-            .toIterable()
-            .extracting(ManageableMailQueue.MailQueueItemView::getMail)
-            .extracting(Mail::getName)
-            .containsExactly("name1");
+        Awaitility.await().untilAsserted(() ->
+                assertThat(getManageableMailQueue().browse())
+                        .toIterable()
+                        
.extracting(ManageableMailQueue.MailQueueItemView::getMail)
+                        .extracting(Mail::getName)
+                        .containsExactly("name1")
+        );
     }
 
     static Stream<Arguments> 
removeByRecipientShouldRemoveSpecificEmailWhenMultipleRecipients() throws 
AddressException {
@@ -630,13 +628,13 @@ public interface ManageableMailQueueContract extends 
MailQueueContract {
 
         getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, 
toRemove.asString());
 
-        awaitRemove();
-
-        assertThat(getManageableMailQueue().browse())
-            .toIterable()
-            .extracting(ManageableMailQueue.MailQueueItemView::getMail)
-            .extracting(Mail::getName)
-            .containsExactly("name2");
+        Awaitility.await().untilAsserted(() ->
+                assertThat(getManageableMailQueue().browse())
+                        .toIterable()
+                        
.extracting(ManageableMailQueue.MailQueueItemView::getMail)
+                        .extracting(Mail::getName)
+                        .containsExactly("name2")
+        );
     }
 
     @Test
@@ -693,7 +691,13 @@ public interface ManageableMailQueueContract extends 
MailQueueContract {
 
         getManageableMailQueue().remove(ManageableMailQueue.Type.Name, 
"name1");
 
-        awaitRemove();
+        Awaitility.await().untilAsserted(() ->
+                assertThat(getManageableMailQueue().browse())
+                        .toIterable()
+                        
.extracting(ManageableMailQueue.MailQueueItemView::getMail)
+                        .extracting(Mail::getName)
+                        .doesNotContain("name1")
+        );
 
         
assertThat(Flux.from(getManageableMailQueue().deQueue()).blockFirst().getMail().getName())
             .isEqualTo("name2");
@@ -709,7 +713,11 @@ public interface ManageableMailQueueContract extends 
MailQueueContract {
 
         getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, 
MailAddressFixture.RECIPIENT1.asString());
 
-        awaitRemove();
+        Awaitility.await().untilAsserted(() ->
+                assertThat(getManageableMailQueue().browse())
+                        .toIterable()
+                        .isEmpty()
+        );
 
         enQueue(defaultMail()
             .name("name2")
@@ -729,7 +737,12 @@ public interface ManageableMailQueueContract extends 
MailQueueContract {
 
         getManageableMailQueue().remove(ManageableMailQueue.Type.Recipient, 
MailAddressFixture.RECIPIENT1.asString());
 
-        awaitRemove();
+        Awaitility.await().untilAsserted(() ->
+                assertThat(getManageableMailQueue().browse())
+                        .toIterable()
+                        .isEmpty()
+        );
+
 
         enQueue(defaultMail()
             .name("name2")
diff --git 
a/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java
 
b/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java
index 135210a630..c11d0bad5f 100644
--- 
a/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java
+++ 
b/server/queue/queue-pulsar/src/test/java/org/apache/james/queue/pulsar/PulsarMailQueueTest.java
@@ -41,7 +41,6 @@ import org.apache.james.blob.api.Store;
 import org.apache.james.blob.mail.MimeMessagePartsId;
 import org.apache.james.blob.mail.MimeMessageStore;
 import org.apache.james.blob.memory.MemoryBlobStoreDAO;
-import org.apache.james.junit.categories.Unstable;
 import org.apache.james.queue.api.DelayedMailQueueContract;
 import org.apache.james.queue.api.DelayedManageableMailQueueContract;
 import org.apache.james.queue.api.MailQueue;
@@ -61,7 +60,6 @@ import org.awaitility.Awaitility;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
@@ -71,7 +69,6 @@ import com.sksamuel.pulsar4s.ConsumerMessage;
 import reactor.core.publisher.Flux;
 import scala.jdk.javaapi.OptionConverters;
 
-@Tag(Unstable.TAG)
 @ExtendWith(DockerPulsarExtension.class)
 public class PulsarMailQueueTest implements MailQueueContract, 
MailQueueMetricContract, ManageableMailQueueContract, DelayedMailQueueContract, 
DelayedManageableMailQueueContract {
 
@@ -110,15 +107,6 @@ public class PulsarMailQueueTest implements 
MailQueueContract, MailQueueMetricCo
         pulsarClients.stop();
     }
 
-    @Override
-    public void awaitRemove() {
-        try {
-            Thread.sleep(50);
-        } catch (InterruptedException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
     @Override
     public MailQueue getMailQueue() {
         return mailQueue;
@@ -272,13 +260,13 @@ public class PulsarMailQueueTest implements 
MailQueueContract, MailQueueMetricCo
         //this won't delete the mail from the store until we try a dequeue
         getManageableMailQueue().remove(ManageableMailQueue.Type.Name, 
"name2");
 
-        awaitRemove();
-
-        assertThat(getManageableMailQueue().browse())
-                .toIterable()
-                .extracting(ManageableMailQueue.MailQueueItemView::getMail)
-                .extracting(Mail::getName)
-                .containsExactly("name1", "name3");
+        Awaitility.await().untilAsserted(() ->
+                assertThat(getManageableMailQueue().browse())
+                        .toIterable()
+                        
.extracting(ManageableMailQueue.MailQueueItemView::getMail)
+                        .extracting(Mail::getName)
+                        .containsExactly("name1", "name3")
+        );
 
         
Flux.from(getMailQueue().deQueue()).take(2).doOnNext(Throwing.consumer(x -> 
x.done(MailQueue.MailQueueItem.CompletionStatus.SUCCESS))).blockLast();
         Awaitility.await().untilAsserted(this::assertThatStoreIsEmpty);


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org

Reply via email to