You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/02/27 03:32:08 UTC

[james-project] 01/08: JAMES-3059 Refactor Cassandra session instrumentation

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

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

commit 17b881e85301d69af4ff584d15452b21179e15a8
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Feb 25 11:23:12 2020 +0700

    JAMES-3059 Refactor Cassandra session instrumentation
---
 .../james/backends/cassandra/CassandraCluster.java |   4 +-
 .../apache/james/backends/cassandra/Scenario.java  | 223 ++++++++++++++++++++
 .../james/backends/cassandra/ScenarioTest.java     |  40 ++++
 .../james/backends/cassandra/TestingSession.java   | 146 +------------
 .../backends/cassandra/TestingSessionTest.java     | 130 +++++++-----
 .../CassandraMailboxManagerConsistencyTest.java    | 226 ++++++++-------------
 .../cassandra/mail/CassandraACLMapperTest.java     |  19 +-
 .../cassandra/mail/CassandraMailboxMapperTest.java | 149 +++++---------
 8 files changed, 494 insertions(+), 443 deletions(-)

diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
index 4b4854b..8df9d59 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
@@ -18,6 +18,8 @@
  ****************************************************************/
 package org.apache.james.backends.cassandra;
 
+import static org.apache.james.backends.cassandra.Scenario.NOTHING;
+
 import java.util.Optional;
 
 import org.apache.james.backends.cassandra.components.CassandraModule;
@@ -82,7 +84,7 @@ public final class CassandraCluster implements AutoCloseable {
 
     @Override
     public void close() {
-        session.resetExecutionHook();
+        session.registerScenario(NOTHING);
         if (!cluster.isClosed()) {
             clearTables();
             closeCluster();
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/Scenario.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/Scenario.java
new file mode 100644
index 0000000..4bc8746
--- /dev/null
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/Scenario.java
@@ -0,0 +1,223 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.backends.cassandra;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Stream;
+
+import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
+
+import com.datastax.driver.core.BoundStatement;
+import com.datastax.driver.core.ResultSetFuture;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.Statement;
+import com.google.common.base.Preconditions;
+
+public class Scenario {
+    @FunctionalInterface
+    interface Behavior {
+        Behavior THROW = (session, statement) -> {
+            RuntimeException injected_failure = new RuntimeException("Injected failure");
+            injected_failure.printStackTrace();
+            throw injected_failure;
+        };
+
+        Behavior EXECUTE_NORMALLY = Session::executeAsync;
+
+        static Behavior awaitOn(Barrier barrier) {
+            return (session, statement) -> {
+                barrier.call();
+                return session.executeAsync(statement);
+            };
+        }
+
+        ResultSetFuture execute(Session session, Statement statement);
+    }
+
+    @FunctionalInterface
+    interface StatementPredicate {
+        class BoundStatementStartingWith implements StatementPredicate {
+            private final String queryStringPrefix;
+
+            BoundStatementStartingWith(String queryStringPrefix) {
+                this.queryStringPrefix = queryStringPrefix;
+            }
+
+            @Override
+            public boolean test(Statement statement) {
+                if (statement instanceof BoundStatement) {
+                    BoundStatement boundStatement = (BoundStatement) statement;
+                    return boundStatement.preparedStatement()
+                        .getQueryString()
+                        .startsWith(queryStringPrefix);
+                }
+                return false;
+            }
+        }
+
+        StatementPredicate ALL_STATEMENTS = statement -> true;
+
+        boolean test(Statement statement);
+    }
+
+    @FunctionalInterface
+    interface Validity {
+        class LimitedValidity implements Validity {
+            final AtomicInteger remaining;
+
+            private LimitedValidity(int applyCount) {
+                Preconditions.checkArgument(applyCount > 0, "'applyCount' needs to be strictly positive");
+                this.remaining = new AtomicInteger(applyCount);
+            }
+
+            @Override
+            public boolean isApplicable() {
+                return remaining.getAndDecrement() > 0;
+            }
+        }
+
+        Validity FOREVER = () -> true;
+
+        boolean isApplicable();
+    }
+
+    public interface Builder {
+        @FunctionalInterface
+        interface RequiresValidity {
+            RequiresStatementPredicate validity(Validity validity);
+
+            default RequiresStatementPredicate forever() {
+                return validity(Validity.FOREVER);
+            }
+
+            default RequiresStatementPredicate times(int applyCount) {
+                return validity(new Validity.LimitedValidity(applyCount));
+            }
+        }
+
+        @FunctionalInterface
+        interface RequiresStatementPredicate {
+            ExecutionHook statementPredicate(StatementPredicate statementPredicate);
+
+            default ExecutionHook forAllQueries() {
+                return statementPredicate(StatementPredicate.ALL_STATEMENTS);
+            }
+
+            default ExecutionHook whenQueryStartsWith(String queryStringPrefix) {
+                return statementPredicate(new StatementPredicate.BoundStatementStartingWith(queryStringPrefix));
+            }
+        }
+
+        static RequiresValidity fail() {
+            return validity -> statementPredicate -> new ExecutionHook(
+                statementPredicate,
+                Behavior.THROW,
+                validity);
+        }
+
+        static RequiresValidity executeNormally() {
+            return validity -> statementPredicate -> new ExecutionHook(
+                statementPredicate,
+                Behavior.EXECUTE_NORMALLY,
+                validity);
+        }
+
+        static RequiresValidity awaitOn(Barrier barrier) {
+            return validity -> statementPredicate -> new ExecutionHook(
+                statementPredicate,
+                Behavior.awaitOn(barrier),
+                validity);
+        }
+    }
+
+    public static class Barrier {
+        private final CountDownLatch callerLatch = new CountDownLatch(1);
+        private final CountDownLatch awaitCallerLatch;
+
+        public Barrier() {
+            this(1);
+        }
+
+        public Barrier(int callerCount) {
+            awaitCallerLatch = new CountDownLatch(callerCount);
+        }
+
+        public void awaitCaller() throws InterruptedException {
+            awaitCallerLatch.await();
+        }
+
+        public void releaseCaller() {
+            callerLatch.countDown();
+        }
+
+        void call() {
+            awaitCallerLatch.countDown();
+            try {
+                callerLatch.await();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public static class ExecutionHook {
+        final StatementPredicate statementPredicate;
+        final Behavior behavior;
+        final Validity validity;
+
+        private ExecutionHook(StatementPredicate statementPredicate, Behavior behavior, Validity validity) {
+            this.statementPredicate = statementPredicate;
+            this.behavior = behavior;
+            this.validity = validity;
+        }
+
+        /**
+         * Returns the behaviour of this hook if it should be applied
+         */
+        Stream<Behavior> asBehavior(Statement statement) {
+            if (statementPredicate.test(statement)) {
+                if (validity.isApplicable()) {
+                    return Stream.of(behavior);
+                }
+            }
+            return Stream.empty();
+        }
+    }
+
+    public static final Scenario NOTHING = new Scenario(ImmutableList.of());
+
+    public static Scenario combine(ExecutionHook... hooks) {
+        return new Scenario(ImmutableList.copyOf(hooks));
+    }
+
+    private final ImmutableList<ExecutionHook> hooks;
+
+    private Scenario(ImmutableList<ExecutionHook> hooks) {
+        this.hooks = hooks;
+    }
+
+    Behavior getCorrespondingBehavior(Statement statement) {
+        return hooks.stream()
+            .flatMap(executionHook -> executionHook.asBehavior(statement))
+            .findFirst()
+            .orElse(Behavior.EXECUTE_NORMALLY);
+    }
+}
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/ScenarioTest.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/ScenarioTest.java
new file mode 100644
index 0000000..c8dc34b
--- /dev/null
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/ScenarioTest.java
@@ -0,0 +1,40 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.backends.cassandra;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+class ScenarioTest {
+    @Test
+    void timesShouldThrowWhenZero() {
+        assertThatThrownBy(() -> Scenario.Builder.fail()
+            .times(0))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void timesShouldThrowWhenNegative() {
+        assertThatThrownBy(() -> Scenario.Builder.fail()
+            .times(-1))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+}
\ No newline at end of file
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/TestingSession.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/TestingSession.java
index d102148..b229b46 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/TestingSession.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/TestingSession.java
@@ -20,11 +20,7 @@
 package org.apache.james.backends.cassandra;
 
 import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Predicate;
 
-import com.datastax.driver.core.BoundStatement;
 import com.datastax.driver.core.CloseFuture;
 import com.datastax.driver.core.Cluster;
 import com.datastax.driver.core.PreparedStatement;
@@ -36,146 +32,20 @@ import com.datastax.driver.core.Statement;
 import com.google.common.util.concurrent.ListenableFuture;
 
 public class TestingSession implements Session {
-    @FunctionalInterface
-    interface Behavior {
-        Behavior THROW = (session, statement) -> {
-            RuntimeException injected_failure = new RuntimeException("Injected failure");
-            injected_failure.printStackTrace();
-            throw injected_failure;
-        };
-
-        Behavior EXECUTE_NORMALLY = Session::executeAsync;
-
-        static Behavior awaitOn(Barrier barrier) {
-            return (session, statement) -> {
-                barrier.call();
-                return session.executeAsync(statement);
-            };
-        }
-
-        ResultSetFuture execute(Session session, Statement statement);
-    }
-
-    public static class Barrier {
-        private final CountDownLatch callerLatch = new CountDownLatch(1);
-        private final CountDownLatch awaitCallerLatch;
-
-        public Barrier() {
-            this(1);
-        }
-
-        public Barrier(int callerCount) {
-            awaitCallerLatch = new CountDownLatch(callerCount);
-        }
-
-        public void awaitCaller() throws InterruptedException {
-            awaitCallerLatch.await();
-        }
-
-        public void releaseCaller() {
-            callerLatch.countDown();
-        }
-
-        void call() {
-            awaitCallerLatch.countDown();
-            try {
-                callerLatch.await();
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    @FunctionalInterface
-    interface StatementPredicate extends Predicate<Statement> {
-
-    }
-
-    static class BoundStatementStartingWith implements StatementPredicate {
-        private final String queryStringPrefix;
-
-        BoundStatementStartingWith(String queryStringPrefix) {
-            this.queryStringPrefix = queryStringPrefix;
-        }
-
-        @Override
-        public boolean test(Statement statement) {
-            if (statement instanceof BoundStatement) {
-                BoundStatement boundStatement = (BoundStatement) statement;
-                return boundStatement.preparedStatement()
-                    .getQueryString()
-                    .startsWith(queryStringPrefix);
-            }
-            return false;
-        }
-    }
-
-    @FunctionalInterface
-    public interface RequiresCondition {
-        RequiresApplyCount condition(StatementPredicate statementPredicate);
-
-        default RequiresApplyCount always() {
-            return condition(ALL_STATEMENTS);
-        }
-
-        default RequiresApplyCount whenBoundStatementStartsWith(String queryStringPrefix) {
-            return condition(new BoundStatementStartingWith(queryStringPrefix));
-        }
-    }
-
-    @FunctionalInterface
-    public interface RequiresApplyCount {
-        FinalStage times(int applyCount);
-    }
-
-    @FunctionalInterface
-    public interface FinalStage {
-        void setExecutionHook();
-    }
-
-    private static class ExecutionHook {
-        final StatementPredicate statementPredicate;
-        final Behavior behavior;
-        final AtomicInteger remaining;
-
-        private ExecutionHook(StatementPredicate statementPredicate, Behavior behavior, int applyCount) {
-            this.statementPredicate = statementPredicate;
-            this.behavior = behavior;
-            this.remaining = new AtomicInteger(applyCount);
-        }
-
-        ResultSetFuture execute(Session session, Statement statement) {
-            if (statementPredicate.test(statement)) {
-                int hookPosition = remaining.getAndDecrement();
-                if (hookPosition > 0) {
-                    return behavior.execute(session, statement);
-                }
-            }
-            return Behavior.EXECUTE_NORMALLY.execute(session, statement);
-        }
-    }
-
-    private static StatementPredicate ALL_STATEMENTS = statement -> true;
-    private static ExecutionHook NO_EXECUTION_HOOK = new ExecutionHook(ALL_STATEMENTS, Behavior.EXECUTE_NORMALLY, 0);
-
     private final Session delegate;
-    private volatile ExecutionHook executionHook;
+    private volatile Scenario scenario;
 
     TestingSession(Session delegate) {
         this.delegate = delegate;
-        this.executionHook = NO_EXECUTION_HOOK;
-    }
-
-    public RequiresCondition fail() {
-        return condition -> applyCount -> () -> executionHook = new ExecutionHook(condition, Behavior.THROW, applyCount);
+        this.scenario = Scenario.NOTHING;
     }
 
-    public RequiresCondition awaitOn(Barrier barrier) {
-        return condition -> applyCount -> () -> executionHook = new ExecutionHook(condition, Behavior.awaitOn(barrier), applyCount);
+    public void registerScenario(Scenario scenario) {
+        this.scenario = scenario;
     }
 
-    public void resetExecutionHook() {
-        executionHook = NO_EXECUTION_HOOK;
+    public void registerScenario(Scenario.ExecutionHook hook) {
+        this.scenario = Scenario.combine(hook);
     }
 
     @Override
@@ -230,7 +100,9 @@ public class TestingSession implements Session {
 
     @Override
     public ResultSetFuture executeAsync(Statement statement) {
-        return executionHook.execute(delegate, statement);
+        return scenario
+            .getCorrespondingBehavior(statement)
+            .execute(delegate, statement);
     }
 
     @Override
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/TestingSessionTest.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/TestingSessionTest.java
index c044697..941c16b 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/TestingSessionTest.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/TestingSessionTest.java
@@ -19,11 +19,15 @@
 
 package org.apache.james.backends.cassandra;
 
+import static org.apache.james.backends.cassandra.Scenario.Builder.awaitOn;
+import static org.apache.james.backends.cassandra.Scenario.Builder.executeNormally;
+import static org.apache.james.backends.cassandra.Scenario.Builder.fail;
+import static org.apache.james.backends.cassandra.Scenario.combine;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-import org.apache.james.backends.cassandra.TestingSession.Barrier;
+import org.apache.james.backends.cassandra.Scenario.Barrier;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
 import org.apache.james.backends.cassandra.versions.SchemaVersion;
@@ -53,48 +57,44 @@ class TestingSessionTest {
     }
 
     @Test
-    void daoOperationShouldNotBeInstrumentedWhenNotMatching(CassandraCluster cassandra) {
+    void daoOperationShouldNotBeInstrumentedWhenExecuteNormally(CassandraCluster cassandra) {
         cassandra.getConf()
-            .fail()
-            .whenBoundStatementStartsWith("non matching")
-            .times(1)
-            .setExecutionHook();
+            .registerScenario(executeNormally()
+                .times(1)
+                .whenQueryStartsWith("SELECT value FROM schemaVersion;"));
 
         assertThatCode(() -> dao.getCurrentSchemaVersion().block())
             .doesNotThrowAnyException();
     }
 
     @Test
-    void daoOperationShouldNotBeInstrumentedWhenTimesIsZero(CassandraCluster cassandra) {
+    void daoOperationShouldNotBeInstrumentedWhenNotMatching(CassandraCluster cassandra) {
         cassandra.getConf()
-            .fail()
-            .whenBoundStatementStartsWith("SELECT value FROM schemaVersion;")
-            .times(0)
-            .setExecutionHook();
+            .registerScenario(fail()
+                .times(1)
+                .whenQueryStartsWith("non matching"));
 
         assertThatCode(() -> dao.getCurrentSchemaVersion().block())
             .doesNotThrowAnyException();
     }
 
     @Test
-    void daoOperationShouldNotBeInstrumentedWhenTimesIsNegative(CassandraCluster cassandra) {
+    void daoOperationShouldFailWhenInstrumented(CassandraCluster cassandra) {
         cassandra.getConf()
-            .fail()
-            .whenBoundStatementStartsWith("SELECT value FROM schemaVersion;")
-            .times(-1)
-            .setExecutionHook();
+            .registerScenario(fail()
+                .times(1)
+                .whenQueryStartsWith("SELECT value FROM schemaVersion;"));
 
-        assertThatCode(() -> dao.getCurrentSchemaVersion().block())
-            .doesNotThrowAnyException();
+        assertThatThrownBy(() -> dao.getCurrentSchemaVersion().block())
+            .isInstanceOf(RuntimeException.class);
     }
 
     @Test
-    void daoOperationShouldFailWhenInstrumented(CassandraCluster cassandra) {
+    void forAllQueriesShouldMatchAllStatements(CassandraCluster cassandra) {
         cassandra.getConf()
-            .fail()
-            .whenBoundStatementStartsWith("SELECT value FROM schemaVersion;")
-            .times(1)
-            .setExecutionHook();
+            .registerScenario(fail()
+                .times(1)
+                .forAllQueries());
 
         assertThatThrownBy(() -> dao.getCurrentSchemaVersion().block())
             .isInstanceOf(RuntimeException.class);
@@ -103,10 +103,9 @@ class TestingSessionTest {
     @Test
     void daoShouldNotBeInstrumentedWhenTimesIsExceeded(CassandraCluster cassandra) {
         cassandra.getConf()
-            .fail()
-            .whenBoundStatementStartsWith("SELECT value FROM schemaVersion;")
-            .times(1)
-            .setExecutionHook();
+            .registerScenario(fail()
+                .times(1)
+                .whenQueryStartsWith("SELECT value FROM schemaVersion;"));
 
         try {
             dao.getCurrentSchemaVersion().block();
@@ -121,10 +120,9 @@ class TestingSessionTest {
     @Test
     void timesShouldSpecifyExactlyTheFailureCount(CassandraCluster cassandra) {
         cassandra.getConf()
-            .fail()
-            .whenBoundStatementStartsWith("SELECT value FROM schemaVersion;")
-            .times(2)
-            .setExecutionHook();
+            .registerScenario(fail()
+                .times(2)
+                .whenQueryStartsWith("SELECT value FROM schemaVersion;"));
 
         SoftAssertions.assertSoftly(softly -> {
             assertThatThrownBy(() -> dao.getCurrentSchemaVersion().block())
@@ -137,26 +135,49 @@ class TestingSessionTest {
     }
 
     @Test
-    void resetExecutionHookShouldClearInstrumentation(CassandraCluster cassandra) {
+    void scenarioShouldDefiningSeveralHooks(CassandraCluster cassandra) {
         cassandra.getConf()
-            .fail()
-            .whenBoundStatementStartsWith("SELECT value FROM schemaVersion;")
-            .times(1)
-            .setExecutionHook();
+            .registerScenario(combine(
+                executeNormally()
+                    .times(1)
+                    .whenQueryStartsWith("SELECT value FROM schemaVersion;"),
+                fail()
+                    .times(1)
+                    .whenQueryStartsWith("SELECT value FROM schemaVersion;")));
+
+        SoftAssertions.assertSoftly(softly -> {
+            assertThatCode(() -> dao.getCurrentSchemaVersion().block())
+                .doesNotThrowAnyException();
+            assertThatThrownBy(() -> dao.getCurrentSchemaVersion().block())
+                .isInstanceOf(RuntimeException.class);
+            assertThatCode(() -> dao.getCurrentSchemaVersion().block())
+                .doesNotThrowAnyException();
+        });
+    }
 
-        cassandra.getConf().resetExecutionHook();
+    @Test
+    void foreverShouldAlwaysApplyBehaviour(CassandraCluster cassandra) {
+        cassandra.getConf()
+            .registerScenario(fail()
+                .forever()
+                .whenQueryStartsWith("SELECT value FROM schemaVersion;"));
 
-        assertThatCode(() -> dao.getCurrentSchemaVersion().block())
-            .doesNotThrowAnyException();
+        SoftAssertions.assertSoftly(softly -> {
+            assertThatThrownBy(() -> dao.getCurrentSchemaVersion().block())
+                .isInstanceOf(RuntimeException.class);
+            assertThatThrownBy(() -> dao.getCurrentSchemaVersion().block())
+                .isInstanceOf(RuntimeException.class);
+            assertThatThrownBy(() -> dao.getCurrentSchemaVersion().block())
+                .isInstanceOf(RuntimeException.class);
+        });
     }
 
     @Test
     void timesShouldBeTakenIntoAccountOnlyForMatchingStatements(CassandraCluster cassandra) {
         cassandra.getConf()
-            .fail()
-            .whenBoundStatementStartsWith("SELECT value FROM schemaVersion;")
-            .times(1)
-            .setExecutionHook();
+            .registerScenario(fail()
+                .times(1)
+                .whenQueryStartsWith("SELECT value FROM schemaVersion;"));
 
         dao.updateVersion(new SchemaVersion(36)).block();
 
@@ -170,10 +191,9 @@ class TestingSessionTest {
         dao.updateVersion(originalSchemaVersion).block();
         Barrier barrier = new Barrier();
         cassandra.getConf()
-            .awaitOn(barrier)
-            .whenBoundStatementStartsWith("INSERT INTO schemaVersion")
-            .times(1)
-            .setExecutionHook();
+            .registerScenario(awaitOn(barrier)
+                .times(1)
+                .whenQueryStartsWith("INSERT INTO schemaVersion"));
 
         dao.updateVersion(new SchemaVersion(36)).subscribeOn(Schedulers.elastic()).subscribe();
 
@@ -184,17 +204,16 @@ class TestingSessionTest {
     }
 
     @Test
-    void statementShouldBeAppliedWhenBarrierIsReleased(CassandraCluster cassandra) throws Exception {
+    void statementShouldBeAppliedWhenBarrierIsReleased(CassandraCluster cassandra) {
         SchemaVersion originalSchemaVersion = new SchemaVersion(32);
         SchemaVersion newVersion = new SchemaVersion(36);
 
         dao.updateVersion(originalSchemaVersion).block();
         Barrier barrier = new Barrier();
         cassandra.getConf()
-            .awaitOn(barrier)
-            .whenBoundStatementStartsWith("INSERT INTO schemaVersion")
-            .times(1)
-            .setExecutionHook();
+            .registerScenario(awaitOn(barrier)
+                .times(1)
+                .whenQueryStartsWith("INSERT INTO schemaVersion"));
 
         Mono<Void> operation = dao.updateVersion(newVersion).cache();
 
@@ -214,10 +233,9 @@ class TestingSessionTest {
         dao.updateVersion(originalSchemaVersion).block();
         Barrier barrier = new Barrier();
         cassandra.getConf()
-            .awaitOn(barrier)
-            .whenBoundStatementStartsWith("INSERT INTO schemaVersion")
-            .times(1)
-            .setExecutionHook();
+            .registerScenario(awaitOn(barrier)
+                .times(1)
+                .whenQueryStartsWith("INSERT INTO schemaVersion"));
 
         Mono<Void> operation = dao.updateVersion(newVersion).cache();
 
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
index 184c0bf..ccd5249 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerConsistencyTest.java
@@ -18,6 +18,8 @@
  ****************************************************************/
 package org.apache.james.mailbox.cassandra;
 
+import static org.apache.james.backends.cassandra.Scenario.Builder.fail;
+import static org.apache.james.backends.cassandra.Scenario.NOTHING;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import org.apache.james.backends.cassandra.CassandraCluster;
@@ -79,15 +81,13 @@ class CassandraMailboxManagerConsistencyTest {
 
         @Test
         void createMailboxShouldBeConsistentWhenMailboxDaoFails(CassandraCluster cassandra) {
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
             doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                 softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
@@ -99,15 +99,13 @@ class CassandraMailboxManagerConsistencyTest {
 
         @Test
         void createMailboxShouldBeConsistentWhenMailboxPathDaoFails(CassandraCluster cassandra) {
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailboxPathV2")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailboxPathV2"));
 
             doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                 softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
@@ -120,11 +118,9 @@ class CassandraMailboxManagerConsistencyTest {
         @Disabled("JAMES-3056 createMailbox() doesn't return mailboxId while it's supposed to")
         @Test
         void createMailboxAfterAFailedCreationShouldCreateTheMailboxWhenMailboxDaoFails(CassandraCluster cassandra) throws Exception {
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
             doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
 
@@ -134,15 +130,13 @@ class CassandraMailboxManagerConsistencyTest {
 
         @Test
         void createMailboxAfterAFailedCreationShouldCreateTheMailboxWhenMailboxPathDaoFails(CassandraCluster cassandra) throws Exception {
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailboxPathV2")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailboxPathV2"));
 
             doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                 .get();
@@ -161,15 +155,13 @@ class CassandraMailboxManagerConsistencyTest {
         @Disabled("JAMES-3056 createMailbox() doesn't return mailboxId while it's supposed to")
         @Test
         void createMailboxAfterDeletingShouldCreateTheMailboxWhenMailboxDaoFails(CassandraCluster cassandra) throws Exception {
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
             doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
@@ -179,15 +171,13 @@ class CassandraMailboxManagerConsistencyTest {
 
         @Test
         void createMailboxAfterDeletingShouldCreateTheMailboxWhenMailboxPathDaoFails(CassandraCluster cassandra) throws Exception {
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailboxPathV2")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailboxPathV2"));
 
             doQuietly(() -> testee.createMailbox(inboxPath, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
@@ -214,15 +204,13 @@ class CassandraMailboxManagerConsistencyTest {
             MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                 .get();
 
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
             doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                 softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
@@ -240,15 +228,13 @@ class CassandraMailboxManagerConsistencyTest {
             MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                 .get();
 
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailboxPathV2")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailboxPathV2"));
 
             doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                 softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
@@ -267,15 +253,13 @@ class CassandraMailboxManagerConsistencyTest {
             MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                 .get();
 
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
             doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             MailboxId newMailboxId = testee.createMailbox(inboxPathRenamed, mailboxSession)
                 .get();
@@ -301,15 +285,13 @@ class CassandraMailboxManagerConsistencyTest {
             MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                 .get();
 
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailboxPathV2")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailboxPathV2"));
 
             doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             MailboxId newMailboxId = testee.createMailbox(inboxPathRenamed, mailboxSession)
                 .get();
@@ -336,15 +318,13 @@ class CassandraMailboxManagerConsistencyTest {
             MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                 .get();
 
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
             doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             testee.deleteMailbox(inboxId, mailboxSession);
             assertThat(testee.createMailbox(inboxPathRenamed, mailboxSession))
@@ -356,15 +336,13 @@ class CassandraMailboxManagerConsistencyTest {
             MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                 .get();
 
-            cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailboxPathV2")
+            cassandra.getConf().registerScenario(fail()
                 .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .whenQueryStartsWith("INSERT INTO mailboxPathV2"));
 
             doQuietly(() -> testee.renameMailbox(inboxPath, inboxPathRenamed, mailboxSession));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(NOTHING);
 
             testee.deleteMailbox(inboxId, mailboxSession);
             MailboxId newMailboxId = testee.createMailbox(inboxPathRenamed, mailboxSession)
@@ -393,15 +371,13 @@ class CassandraMailboxManagerConsistencyTest {
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                     softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
@@ -420,15 +396,13 @@ class CassandraMailboxManagerConsistencyTest {
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                     softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
@@ -446,15 +420,13 @@ class CassandraMailboxManagerConsistencyTest {
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                     softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
@@ -472,15 +444,13 @@ class CassandraMailboxManagerConsistencyTest {
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                     softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
@@ -501,15 +471,13 @@ class CassandraMailboxManagerConsistencyTest {
             void createMailboxShouldCreateWhenMailboxDaoFailsOnDeleteByPath(CassandraCluster cassandra) throws Exception {
                 testee.createMailbox(inboxPath, mailboxSession);
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
@@ -530,15 +498,13 @@ class CassandraMailboxManagerConsistencyTest {
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
@@ -559,15 +525,13 @@ class CassandraMailboxManagerConsistencyTest {
             void createMailboxShouldCreateWhenMailboxPathDaoFailsOnDeleteByPath(CassandraCluster cassandra) throws Exception {
                 testee.createMailbox(inboxPath, mailboxSession);
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
@@ -589,15 +553,13 @@ class CassandraMailboxManagerConsistencyTest {
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
@@ -622,20 +584,16 @@ class CassandraMailboxManagerConsistencyTest {
             void deleteMailboxByPathShouldDeleteWhenMailboxDaoFails(CassandraCluster cassandra) throws Exception {
                 testee.createMailbox(inboxPath, mailboxSession);
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
-
                 SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                     softly.assertThat(testee.search(allMailboxesSearchQuery, mailboxSession))
                         .isEmpty();
@@ -649,15 +607,13 @@ class CassandraMailboxManagerConsistencyTest {
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
 
@@ -673,15 +629,13 @@ class CassandraMailboxManagerConsistencyTest {
             void deleteMailboxByPathShouldDeleteWhenMailboxPathDaoFails(CassandraCluster cassandra) throws Exception {
                 testee.createMailbox(inboxPath, mailboxSession);
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
@@ -698,15 +652,13 @@ class CassandraMailboxManagerConsistencyTest {
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
 
@@ -726,15 +678,13 @@ class CassandraMailboxManagerConsistencyTest {
             void createMailboxShouldCreateWhenMailboxDaoFailsOnDeleteByPath(CassandraCluster cassandra) throws Exception {
                 testee.createMailbox(inboxPath, mailboxSession);
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
@@ -756,15 +706,13 @@ class CassandraMailboxManagerConsistencyTest {
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
                 MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
@@ -785,15 +733,13 @@ class CassandraMailboxManagerConsistencyTest {
             void createMailboxShouldCreateWhenMailboxPathDaoFailsOnDeleteByPath(CassandraCluster cassandra) throws Exception {
                 testee.createMailbox(inboxPath, mailboxSession);
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 doQuietly(() -> testee.deleteMailbox(inboxPath, mailboxSession));
                 MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
@@ -815,15 +761,13 @@ class CassandraMailboxManagerConsistencyTest {
                 MailboxId inboxId = testee.createMailbox(inboxPath, mailboxSession)
                     .get();
 
-                cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2")
+                cassandra.getConf().registerScenario(fail()
                     .times(TRY_COUNT_BEFORE_FAILURE)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2"));
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
 
-                cassandra.getConf().resetExecutionHook();
+                cassandra.getConf().registerScenario(NOTHING);
 
                 doQuietly(() -> testee.deleteMailbox(inboxId, mailboxSession));
                 MailboxId inboxNewId = testee.createMailbox(inboxPath, mailboxSession)
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraACLMapperTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraACLMapperTest.java
index 025a41a..cf5a4ca 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraACLMapperTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraACLMapperTest.java
@@ -19,10 +19,10 @@
 package org.apache.james.mailbox.cassandra.mail;
 
 import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
+import static org.apache.james.backends.cassandra.Scenario.Builder.awaitOn;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.UUID;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -33,7 +33,7 @@ import java.util.concurrent.TimeoutException;
 
 import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.CassandraClusterExtension;
-import org.apache.james.backends.cassandra.TestingSession.Barrier;
+import org.apache.james.backends.cassandra.Scenario.Barrier;
 import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
 import org.apache.james.mailbox.cassandra.ids.CassandraId;
@@ -163,10 +163,9 @@ class CassandraACLMapperTest {
     void twoConcurrentUpdatesWhenNoACLStoredShouldReturnACLWithTwoEntries(CassandraCluster cassandra) throws Exception {
         Barrier barrier = new Barrier(2);
         cassandra.getConf()
-            .awaitOn(barrier)
-            .whenBoundStatementStartsWith("SELECT acl,version FROM acl WHERE id=:id;")
-            .times(2)
-            .setExecutionHook();
+            .registerScenario(awaitOn(barrier)
+                    .times(2)
+                    .whenQueryStartsWith("SELECT acl,version FROM acl WHERE id=:id;"));
 
         MailboxACL.EntryKey keyBob = new MailboxACL.EntryKey("bob", MailboxACL.NameType.user, false);
         MailboxACL.Rfc4314Rights rights = new MailboxACL.Rfc4314Rights(MailboxACL.Right.Read);
@@ -185,17 +184,15 @@ class CassandraACLMapperTest {
 
     @Test
     void twoConcurrentUpdatesWhenStoredShouldReturnACLWithTwoEntries(CassandraCluster cassandra) throws Exception {
-        CountDownLatch countDownLatch = new CountDownLatch(2);
         MailboxACL.EntryKey keyBenwa = new MailboxACL.EntryKey("benwa", MailboxACL.NameType.user, false);
         MailboxACL.Rfc4314Rights rights = new MailboxACL.Rfc4314Rights(MailboxACL.Right.Read);
         cassandraACLMapper.updateACL(MAILBOX_ID, MailboxACL.command().key(keyBenwa).rights(rights).asAddition());
 
         Barrier barrier = new Barrier(2);
         cassandra.getConf()
-            .awaitOn(barrier)
-            .whenBoundStatementStartsWith("SELECT acl,version FROM acl WHERE id=:id;")
-            .times(2)
-            .setExecutionHook();
+            .registerScenario(awaitOn(barrier)
+                .times(2)
+                .whenQueryStartsWith("SELECT acl,version FROM acl WHERE id=:id;"));
 
         MailboxACL.EntryKey keyBob = new MailboxACL.EntryKey("bob", MailboxACL.NameType.user, false);
         MailboxACL.EntryKey keyAlice = new MailboxACL.EntryKey("alice", MailboxACL.NameType.user, false);
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java
index 74e27fa..a8af418 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.mailbox.cassandra.mail;
 
+import static org.apache.james.backends.cassandra.Scenario.Builder.fail;
 import static org.apache.james.mailbox.model.MailboxAssertingTool.softly;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -28,6 +29,7 @@ import java.util.List;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.backends.cassandra.Scenario;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
@@ -140,15 +142,12 @@ class CassandraMailboxMapperTest {
                 Mailbox inboxRenamed = createInboxRenamedMailbox(inboxId);
 
                 cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2 WHERE namespace=:namespace AND user=:user AND mailboxName=:mailboxName IF EXISTS;")
-                    .times(1)
-                    .setExecutionHook();
+                    .registerScenario(fail()
+                        .times(1)
+                        .whenQueryStartsWith("DELETE FROM mailboxPathV2 WHERE namespace=:namespace AND user=:user AND mailboxName=:mailboxName IF EXISTS;"));
 
                 testee.rename(inboxRenamed);
 
-                cassandra.getConf().resetExecutionHook();
-
                 SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                     softly(softly)
                         .assertThat(testee.findMailboxById(inboxId))
@@ -170,15 +169,12 @@ class CassandraMailboxMapperTest {
                 Mailbox inboxRenamed = createInboxRenamedMailbox(inboxId);
 
                 cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
+                    .registerScenario(fail()
                     .times(1)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
                 testee.rename(inboxRenamed);
 
-                cassandra.getConf().resetExecutionHook();
-
                 SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                     softly(softly)
                         .assertThat(testee.findMailboxById(inboxId))
@@ -196,15 +192,12 @@ class CassandraMailboxMapperTest {
             @Test
             void createShouldRetryFailedMailboxSaving(CassandraCluster cassandra) throws Exception {
                 cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
-                    .times(1)
-                    .setExecutionHook();
+                    .registerScenario(fail()
+                        .times(1)
+                        .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
                 Mailbox inbox = testee.create(inboxPath, UID_VALIDITY);
 
-                cassandra.getConf().resetExecutionHook();
-
                 SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                     softly(softly)
                         .assertThat(testee.findMailboxById(inbox.getMailboxId()))
@@ -224,15 +217,12 @@ class CassandraMailboxMapperTest {
                 Mailbox inbox = testee.create(inboxPath, UID_VALIDITY);
 
                 cassandra.getConf()
-                    .fail()
-                    .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
+                    .registerScenario(fail()
                     .times(1)
-                    .setExecutionHook();
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
                 testee.delete(inbox);
 
-                cassandra.getConf().resetExecutionHook();
-
                 SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                     assertThatThrownBy(() -> testee.findMailboxById(inbox.getMailboxId()))
                         .isInstanceOf(MailboxNotFoundException.class);
@@ -247,15 +237,12 @@ class CassandraMailboxMapperTest {
         @Test
         void createShouldBeConsistentWhenFailToPersistMailbox(CassandraCluster cassandra) {
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
-                .times(10)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(10)
+                    .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
             doQuietly(() -> testee.create(inboxPath, UID_VALIDITY));
 
-            cassandra.getConf().resetExecutionHook();
-
             SoftAssertions.assertSoftly(softly -> {
                 softly.assertThatThrownBy(() -> testee.findMailboxByPath(inboxPath))
                     .isInstanceOf(MailboxNotFoundException.class);
@@ -273,14 +260,13 @@ class CassandraMailboxMapperTest {
             Mailbox inboxRenamed = createInboxRenamedMailbox(inboxId);
 
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("SELECT id,mailboxbase,uidvalidity,name FROM mailbox WHERE id=:id;")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("SELECT id,mailboxbase,uidvalidity,name FROM mailbox WHERE id=:id;"));
 
             doQuietly(() -> testee.rename(inboxRenamed));
 
-            cassandra.getConf().resetExecutionHook();
+            cassandra.getConf().registerScenario(Scenario.NOTHING);
 
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                 softly(softly)
@@ -304,15 +290,12 @@ class CassandraMailboxMapperTest {
             Mailbox inboxRenamed = createInboxRenamedMailbox(inboxId);
 
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("SELECT id,mailboxbase,uidvalidity,name FROM mailbox WHERE id=:id;")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("SELECT id,mailboxbase,uidvalidity,name FROM mailbox WHERE id=:id;"));
 
             doQuietly(() -> testee.rename(inboxRenamed));
 
-            cassandra.getConf().resetExecutionHook();
-
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                 softly.assertThat(testee.findMailboxWithPathLike(allMailboxesSearchQuery))
                     .hasOnlyOneElementSatisfying(searchMailbox -> softly(softly)
@@ -329,15 +312,12 @@ class CassandraMailboxMapperTest {
             Mailbox inboxRenamed = createInboxRenamedMailbox(inboxId);
 
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("SELECT id,mailboxbase,uidvalidity,name FROM mailbox WHERE id=:id;")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("SELECT id,mailboxbase,uidvalidity,name FROM mailbox WHERE id=:id;"));
 
             doQuietly(() -> testee.rename(inboxRenamed));
 
-            cassandra.getConf().resetExecutionHook();
-
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                 softly.assertThatThrownBy(() -> testee.findMailboxByPath(inboxPathRenamed))
                     .isInstanceOf(MailboxNotFoundException.class);
@@ -353,15 +333,12 @@ class CassandraMailboxMapperTest {
             Mailbox inboxRenamed = createInboxRenamedMailbox(inboxId);
 
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2 WHERE namespace=:namespace AND user=:user AND mailboxName=:mailboxName IF EXISTS;")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2 WHERE namespace=:namespace AND user=:user AND mailboxName=:mailboxName IF EXISTS;"));
 
             doQuietly(() -> testee.rename(inboxRenamed));
 
-            cassandra.getConf().resetExecutionHook();
-
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                 softly(softly)
                     .assertThat(testee.findMailboxById(inboxId))
@@ -384,10 +361,9 @@ class CassandraMailboxMapperTest {
             Mailbox inboxRenamed = createInboxRenamedMailbox(inboxId);
 
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2 WHERE namespace=:namespace AND user=:user AND mailboxName=:mailboxName IF EXISTS;")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2 WHERE namespace=:namespace AND user=:user AND mailboxName=:mailboxName IF EXISTS;"));
 
             doQuietly(() -> testee.rename(inboxRenamed));
 
@@ -407,15 +383,12 @@ class CassandraMailboxMapperTest {
             Mailbox inboxRenamed = createInboxRenamedMailbox(inboxId);
 
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2 WHERE namespace=:namespace AND user=:user AND mailboxName=:mailboxName IF EXISTS;")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2 WHERE namespace=:namespace AND user=:user AND mailboxName=:mailboxName IF EXISTS;"));
 
             doQuietly(() -> testee.rename(inboxRenamed));
 
-            cassandra.getConf().resetExecutionHook();
-
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                 softly.assertThatThrownBy(() -> testee.findMailboxByPath(inboxPathRenamed))
                     .isInstanceOf(MailboxNotFoundException.class);
@@ -431,15 +404,12 @@ class CassandraMailboxMapperTest {
             CassandraId inboxId = (CassandraId) inbox.getMailboxId();
 
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
             doQuietly(() -> testee.delete(inbox));
 
-            cassandra.getConf().resetExecutionHook();
-
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
                 softly.assertThatCode(() -> testee.findMailboxById(inboxId))
                     .doesNotThrowAnyException();
@@ -479,15 +449,12 @@ class CassandraMailboxMapperTest {
         @Test
         void createAfterPreviousFailedCreateShouldCreateAMailbox(CassandraCluster cassandra) throws MailboxException {
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
             doQuietly(() -> testee.create(inboxPath, UID_VALIDITY));
 
-            cassandra.getConf().resetExecutionHook();
-
             Mailbox inbox = testee.create(inboxPath, UID_VALIDITY);
 
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
@@ -508,16 +475,13 @@ class CassandraMailboxMapperTest {
         @Test
         void createAfterPreviousDeleteOnFailedCreateShouldCreateAMailbox(CassandraCluster cassandra) throws MailboxException {
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("INSERT INTO mailbox (id,name,uidvalidity,mailboxbase) VALUES (:id,:name,:uidvalidity,:mailboxbase);"));
 
             doQuietly(() -> testee.create(inboxPath, UID_VALIDITY));
             doQuietly(() -> testee.delete(new Mailbox(inboxPath, UID_VALIDITY, CassandraId.timeBased())));
 
-            cassandra.getConf().resetExecutionHook();
-
             Mailbox inbox = testee.create(inboxPath, UID_VALIDITY);
 
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
@@ -541,15 +505,12 @@ class CassandraMailboxMapperTest {
             CassandraId inboxId = (CassandraId) inbox.getMailboxId();
 
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("DELETE FROM mailbox WHERE id=:id;")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("DELETE FROM mailbox WHERE id=:id;"));
 
             doQuietly(() -> testee.delete(inbox));
 
-            cassandra.getConf().resetExecutionHook();
-
             doQuietly(() -> testee.delete(inbox));
 
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
@@ -573,15 +534,12 @@ class CassandraMailboxMapperTest {
             Mailbox inboxRenamed = createInboxRenamedMailbox(inboxId);
 
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("SELECT id,mailboxbase,uidvalidity,name FROM mailbox WHERE id=:id;")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("SELECT id,mailboxbase,uidvalidity,name FROM mailbox WHERE id=:id;"));
 
             doQuietly(() -> testee.rename(inboxRenamed));
 
-            cassandra.getConf().resetExecutionHook();
-
             doQuietly(() -> testee.rename(inboxRenamed));
 
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
@@ -612,15 +570,12 @@ class CassandraMailboxMapperTest {
             Mailbox inboxRenamed = createInboxRenamedMailbox(inboxId);
 
             cassandra.getConf()
-                .fail()
-                .whenBoundStatementStartsWith("DELETE FROM mailboxPathV2 WHERE namespace=:namespace AND user=:user AND mailboxName=:mailboxName IF EXISTS;")
-                .times(TRY_COUNT_BEFORE_FAILURE)
-                .setExecutionHook();
+                .registerScenario(fail()
+                    .times(TRY_COUNT_BEFORE_FAILURE)
+                    .whenQueryStartsWith("DELETE FROM mailboxPathV2 WHERE namespace=:namespace AND user=:user AND mailboxName=:mailboxName IF EXISTS;"));
 
             doQuietly(() -> testee.rename(inboxRenamed));
 
-            cassandra.getConf().resetExecutionHook();
-
             doQuietly(() -> testee.rename(inboxRenamed));
 
             SoftAssertions.assertSoftly(Throwing.consumer(softly -> {


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org