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 rc...@apache.org on 2020/08/17 09:11:19 UTC

[james-project] 08/26: [Refactoring] Migrate AbstractRecipientRewriteTableTest to RecipientRewriteTableContract in JUnit5

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

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

commit 8b3a128a8525282390dbfafdeed8cba66ec2ea6c
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Thu Aug 13 14:50:40 2020 +0700

    [Refactoring] Migrate AbstractRecipientRewriteTableTest to RecipientRewriteTableContract in JUnit5
---
 .../CassandraRecipientRewriteTableV6Test.java      |  62 +-
 ...ndraRecipientRewriteTableV7BeforeStartTest.java |  63 +-
 .../CassandraRecipientRewriteTableV7Test.java      |  64 +-
 .../rrt/file/XMLRecipientRewriteTableTest.java     | 109 ++--
 .../rrt/jpa/JPARecipientRewriteTableTest.java      |  35 +-
 .../rrt/lib/AbstractRecipientRewriteTableTest.java | 643 ---------------------
 .../rrt/lib/RecipientRewriteTableContract.java     | 636 ++++++++++++++++++++
 .../memory/MemoryRecipientRewriteTableTest.java    |  31 +-
 8 files changed, 831 insertions(+), 812 deletions(-)

diff --git a/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV6Test.java b/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV6Test.java
index 525eacb..8ef69ef 100644
--- a/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV6Test.java
+++ b/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV6Test.java
@@ -20,7 +20,7 @@
 package org.apache.james.rrt.cassandra;
 
 import org.apache.james.backends.cassandra.CassandraCluster;
-import org.apache.james.backends.cassandra.DockerCassandraRule;
+import org.apache.james.backends.cassandra.CassandraClusterExtension;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
@@ -28,46 +28,50 @@ import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionManage
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
 import org.apache.james.backends.cassandra.versions.SchemaVersion;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
-import org.apache.james.rrt.lib.AbstractRecipientRewriteTableTest;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
+import org.apache.james.rrt.lib.RecipientRewriteTableContract;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.RegisterExtension;
 
-public class CassandraRecipientRewriteTableV6Test extends AbstractRecipientRewriteTableTest {
-    private static final SchemaVersion SCHEMA_VERSION_V6 = new SchemaVersion(6);
+class CassandraRecipientRewriteTableV6Test implements RecipientRewriteTableContract {
+    static final SchemaVersion SCHEMA_VERSION_V6 = new SchemaVersion(6);
 
-    private static final CassandraModule MODULE = CassandraModule.aggregateModules(
+    static final CassandraModule MODULE = CassandraModule.aggregateModules(
         CassandraRRTModule.MODULE,
         CassandraSchemaVersionModule.MODULE);
 
-    @Rule
-    public DockerCassandraRule cassandraServer = new DockerCassandraRule().allowRestart();
+    @RegisterExtension
+    static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(MODULE);
 
-    protected CassandraCluster cassandra;
+    AbstractRecipientRewriteTable recipientRewriteTable;
+    CassandraRecipientRewriteTableDAO recipientRewriteTableDAO;
+    CassandraMappingsSourcesDAO mappingsSourcesDAO;
+    CassandraSchemaVersionManager schemaVersionManager;
 
-    @Override
-    @Before
-    public void setUp() throws Exception {
-        cassandra = CassandraCluster.create(MODULE, cassandraServer.getHost());
-        super.setUp();
+    @BeforeEach
+    void setup(CassandraCluster cassandra) throws Exception {
+        CassandraSchemaVersionDAO cassandraSchemaVersionDAO = new CassandraSchemaVersionDAO(cassandra.getConf());
+        cassandraSchemaVersionDAO.updateVersion(SCHEMA_VERSION_V6).block();
+
+        recipientRewriteTableDAO = new CassandraRecipientRewriteTableDAO(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION);
+        mappingsSourcesDAO = new CassandraMappingsSourcesDAO(cassandra.getConf());
+        schemaVersionManager = new CassandraSchemaVersionManager(cassandraSchemaVersionDAO);
+
+        setUp();
     }
 
-    @Override
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
-        cassandra.close();
+    @AfterEach
+    void teardown() throws Exception {
+        tearDown();
     }
 
     @Override
-    protected AbstractRecipientRewriteTable getRecipientRewriteTable() {
-        CassandraSchemaVersionDAO cassandraSchemaVersionDAO = new CassandraSchemaVersionDAO(
-            cassandra.getConf());
-        cassandraSchemaVersionDAO.updateVersion(SCHEMA_VERSION_V6).block();
+    public void createRecipientRewriteTable() {
+        recipientRewriteTable = new CassandraRecipientRewriteTable(recipientRewriteTableDAO, mappingsSourcesDAO, schemaVersionManager);
+    }
 
-        return new CassandraRecipientRewriteTable(
-            new CassandraRecipientRewriteTableDAO(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION),
-            new CassandraMappingsSourcesDAO(cassandra.getConf()),
-            new CassandraSchemaVersionManager(cassandraSchemaVersionDAO));
+    @Override
+    public AbstractRecipientRewriteTable virtualUserTable() {
+        return recipientRewriteTable;
     }
 }
diff --git a/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV7BeforeStartTest.java b/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV7BeforeStartTest.java
index 8510f4e..91d48a9 100644
--- a/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV7BeforeStartTest.java
+++ b/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV7BeforeStartTest.java
@@ -20,7 +20,7 @@
 package org.apache.james.rrt.cassandra;
 
 import org.apache.james.backends.cassandra.CassandraCluster;
-import org.apache.james.backends.cassandra.DockerCassandraRule;
+import org.apache.james.backends.cassandra.CassandraClusterExtension;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
@@ -28,47 +28,50 @@ import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionManage
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
 import org.apache.james.backends.cassandra.versions.SchemaVersion;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
-import org.apache.james.rrt.lib.AbstractRecipientRewriteTableTest;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
+import org.apache.james.rrt.lib.RecipientRewriteTableContract;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.RegisterExtension;
 
-public class CassandraRecipientRewriteTableV7BeforeStartTest extends AbstractRecipientRewriteTableTest {
-    private static final SchemaVersion SCHEMA_VERSION_V7 = new SchemaVersion(7);
+class CassandraRecipientRewriteTableV7BeforeStartTest implements RecipientRewriteTableContract {
+    static final SchemaVersion SCHEMA_VERSION_V7 = new SchemaVersion(7);
 
-    private static final CassandraModule MODULE = CassandraModule.aggregateModules(
+    static final CassandraModule MODULE = CassandraModule.aggregateModules(
         CassandraRRTModule.MODULE,
         CassandraSchemaVersionModule.MODULE);
 
-    @Rule
-    public DockerCassandraRule cassandraServer = new DockerCassandraRule().allowRestart();
+    @RegisterExtension
+    static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(MODULE);
 
-    protected CassandraCluster cassandra;
+    AbstractRecipientRewriteTable recipientRewriteTable;
+    CassandraRecipientRewriteTableDAO recipientRewriteTableDAO;
+    CassandraMappingsSourcesDAO mappingsSourcesDAO;
+    CassandraSchemaVersionManager schemaVersionManager;
 
-    @Override
-    @Before
-    public void setUp() throws Exception {
-        cassandra = CassandraCluster.create(MODULE, cassandraServer.getHost());
-        super.setUp();
+    @BeforeEach
+    void setup(CassandraCluster cassandra) throws Exception {
+        CassandraSchemaVersionDAO cassandraSchemaVersionDAO = new CassandraSchemaVersionDAO(cassandra.getConf());
+        cassandraSchemaVersionDAO.updateVersion(SCHEMA_VERSION_V7).block();
+
+        recipientRewriteTableDAO = new CassandraRecipientRewriteTableDAO(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION);
+        mappingsSourcesDAO = new CassandraMappingsSourcesDAO(cassandra.getConf());
+        schemaVersionManager = new CassandraSchemaVersionManager(cassandraSchemaVersionDAO);
+
+        setUp();
     }
 
-    @Override
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
-        cassandra.close();
+    @AfterEach
+    void teardown() throws Exception {
+        tearDown();
     }
 
     @Override
-    protected AbstractRecipientRewriteTable getRecipientRewriteTable() {
-        CassandraSchemaVersionDAO cassandraSchemaVersionDAO = new CassandraSchemaVersionDAO(
-            cassandra.getConf());
-
-        cassandraSchemaVersionDAO.updateVersion(SCHEMA_VERSION_V7).block();
+    public void createRecipientRewriteTable() {
+        recipientRewriteTable = new CassandraRecipientRewriteTable(recipientRewriteTableDAO, mappingsSourcesDAO, schemaVersionManager);
+    }
 
-        return new CassandraRecipientRewriteTable(
-            new CassandraRecipientRewriteTableDAO(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION),
-            new CassandraMappingsSourcesDAO(cassandra.getConf()),
-            new CassandraSchemaVersionManager(cassandraSchemaVersionDAO));
+    @Override
+    public AbstractRecipientRewriteTable virtualUserTable() {
+        return recipientRewriteTable;
     }
 }
diff --git a/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV7Test.java b/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV7Test.java
index 35246b9..1340785 100644
--- a/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV7Test.java
+++ b/server/data/data-cassandra/src/test/java/org/apache/james/rrt/cassandra/CassandraRecipientRewriteTableV7Test.java
@@ -20,7 +20,7 @@
 package org.apache.james.rrt.cassandra;
 
 import org.apache.james.backends.cassandra.CassandraCluster;
-import org.apache.james.backends.cassandra.DockerCassandraRule;
+import org.apache.james.backends.cassandra.CassandraClusterExtension;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
@@ -28,49 +28,53 @@ import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionManage
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
 import org.apache.james.backends.cassandra.versions.SchemaVersion;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
-import org.apache.james.rrt.lib.AbstractRecipientRewriteTableTest;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
+import org.apache.james.rrt.lib.RecipientRewriteTableContract;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.RegisterExtension;
 
-public class CassandraRecipientRewriteTableV7Test extends AbstractRecipientRewriteTableTest {
-    private static final SchemaVersion SCHEMA_VERSION_V7 = new SchemaVersion(7);
+class CassandraRecipientRewriteTableV7Test implements RecipientRewriteTableContract {
+    static final SchemaVersion SCHEMA_VERSION_V7 = new SchemaVersion(7);
 
-    private static final CassandraModule MODULE = CassandraModule.aggregateModules(
+    static final CassandraModule MODULE = CassandraModule.aggregateModules(
         CassandraRRTModule.MODULE,
         CassandraSchemaVersionModule.MODULE);
 
-    @Rule
-    public DockerCassandraRule cassandraServer = new DockerCassandraRule().allowRestart();
+    @RegisterExtension
+    static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(MODULE);
 
-    protected CassandraCluster cassandra;
+    AbstractRecipientRewriteTable recipientRewriteTable;
+    CassandraRecipientRewriteTableDAO recipientRewriteTableDAO;
+    CassandraMappingsSourcesDAO mappingsSourcesDAO;
+    CassandraSchemaVersionManager schemaVersionManager;
+    CassandraSchemaVersionDAO cassandraSchemaVersionDAO;
 
-    @Override
-    @Before
-    public void setUp() throws Exception {
-        cassandra = CassandraCluster.create(MODULE, cassandraServer.getHost());
-        super.setUp();
+    @BeforeEach
+    void setup(CassandraCluster cassandra) throws Exception {
+        cassandraSchemaVersionDAO = new CassandraSchemaVersionDAO(cassandra.getConf());
+        recipientRewriteTableDAO = new CassandraRecipientRewriteTableDAO(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION);
+        mappingsSourcesDAO = new CassandraMappingsSourcesDAO(cassandra.getConf());
+        schemaVersionManager = new CassandraSchemaVersionManager(cassandraSchemaVersionDAO);
+
+        setUp();
     }
 
-    @Override
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
-        cassandra.close();
+    @AfterEach
+    void teardown() throws Exception {
+        tearDown();
     }
 
     @Override
-    protected AbstractRecipientRewriteTable getRecipientRewriteTable() {
-        CassandraSchemaVersionDAO cassandraSchemaVersionDAO = new CassandraSchemaVersionDAO(
-            cassandra.getConf());
-
-        CassandraRecipientRewriteTable rrt = new CassandraRecipientRewriteTable(
-            new CassandraRecipientRewriteTableDAO(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION),
-            new CassandraMappingsSourcesDAO(cassandra.getConf()),
-            new CassandraSchemaVersionManager(cassandraSchemaVersionDAO));
+    public void createRecipientRewriteTable() {
+        CassandraRecipientRewriteTable rrt = new CassandraRecipientRewriteTable(recipientRewriteTableDAO, mappingsSourcesDAO, schemaVersionManager);
 
         cassandraSchemaVersionDAO.updateVersion(SCHEMA_VERSION_V7).block();
 
-        return rrt;
+        recipientRewriteTable = rrt;
+    }
+
+    @Override
+    public AbstractRecipientRewriteTable virtualUserTable() {
+        return recipientRewriteTable;
     }
 }
diff --git a/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java b/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java
index 7ef0a37..d3bd13a 100644
--- a/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java
+++ b/server/data/data-file/src/test/java/org/apache/james/rrt/file/XMLRecipientRewriteTableTest.java
@@ -21,222 +21,227 @@ package org.apache.james.rrt.file;
 import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
 import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
-import org.apache.james.rrt.lib.AbstractRecipientRewriteTableTest;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
+import org.apache.james.rrt.lib.RecipientRewriteTableContract;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
 
-public class XMLRecipientRewriteTableTest extends AbstractRecipientRewriteTableTest {
+class XMLRecipientRewriteTableTest implements RecipientRewriteTableContract {
 
-    private final BaseHierarchicalConfiguration defaultConfiguration = new BaseHierarchicalConfiguration();
+    final BaseHierarchicalConfiguration defaultConfiguration = new BaseHierarchicalConfiguration();
 
-    @Override
-    @Before
-    public void setUp() throws Exception {
+    AbstractRecipientRewriteTable recipientRewriteTable;
+
+    @BeforeEach
+    void setup() throws Exception {
         defaultConfiguration.setListDelimiterHandler(new DisabledListDelimiterHandler());
-        super.setUp();
+        setUp();
+    }
+
+    @AfterEach
+    void teardown() throws Exception {
+        tearDown();
     }
 
     @Override
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
+    public void createRecipientRewriteTable() {
+        recipientRewriteTable = new XMLRecipientRewriteTable();
     }
 
     @Override
-    protected AbstractRecipientRewriteTable getRecipientRewriteTable() {
-        return new XMLRecipientRewriteTable();
+    public AbstractRecipientRewriteTable virtualUserTable() {
+        return recipientRewriteTable;
     }
 
     @Test
-    @Ignore("addMapping doesn't handle checking for domain existence in this test implementation")
+    @Disabled("addMapping doesn't handle checking for domain existence in this test implementation")
     @Override
     public void addAddressMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
     }
 
     @Test
-    @Ignore("addMapping doesn't handle checking for duplicate in this test implementation")
+    @Disabled("addMapping doesn't handle checking for duplicate in this test implementation")
     @Override
     public void addMappingShouldThrowWhenMappingAlreadyExists() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void testStoreAndGetMappings() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void testStoreAndRetrieveRegexMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void getAllMappingsShouldListAllEntries() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void testStoreAndRetrieveAddressMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void testStoreAndRetrieveErrorMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void testStoreAndRetrieveWildCardAddressMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void testNonRecursiveMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void testAliasDomainMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void addMappingShouldNotThrowWhenMappingAlreadyExistsWithAnOtherType() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void addForwardMappingShouldStore() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void removeForwardMappingShouldDelete() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void addGroupMappingShouldStore() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void removeGroupMappingShouldDelete() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void addAliasMappingShouldStore() {
 
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void removeAliasMappingShouldDelete() {
 
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldReturnWhenHasMapping() {
 
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldReturnWhenMultipleSourceMapping() {
 
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldReturnWhenHasForwardMapping() {
 
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldReturnAliasMappings() {
 
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldReturnWhenHasAddressMapping() {
 
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldThrowExceptionWhenHasRegexMapping() {
 
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldHandleDomainMapping() {
 
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldHandleDomainSource() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldHandleDomainSources() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldThrowExceptionWhenHasErrorMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void listSourcesShouldReturnEmptyWhenMappingDoesNotExist() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void getSourcesForTypeShouldReturnEmptyWhenNoMatchingMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void getSourcesForTypeShouldReturnMatchingMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void getSourcesForTypeShouldNotReturnDuplicatedSources() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void getSourcesForTypeShouldReturnSortedStream() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void getMappingsForTypeShouldReturnEmptyWhenNoMatchingMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void getMappingsForTypeShouldReturnMatchingMapping() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void getMappingsForTypeShouldNotReturnDuplicatedDestinations() {
     }
 
     @Test
-    @Ignore("XMLRecipientRewriteTable is read only")
+    @Disabled("XMLRecipientRewriteTable is read only")
     public void getMappingsForTypeShouldReturnSortedStream() {
     }
 }
diff --git a/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPARecipientRewriteTableTest.java b/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPARecipientRewriteTableTest.java
index 7a54550..777ebf7 100644
--- a/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPARecipientRewriteTableTest.java
+++ b/server/data/data-jpa/src/test/java/org/apache/james/rrt/jpa/JPARecipientRewriteTableTest.java
@@ -21,30 +21,35 @@ package org.apache.james.rrt.jpa;
 import org.apache.james.backends.jpa.JpaTestCluster;
 import org.apache.james.rrt.jpa.model.JPARecipientRewrite;
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
-import org.apache.james.rrt.lib.AbstractRecipientRewriteTableTest;
-import org.junit.After;
-import org.junit.Before;
+import org.apache.james.rrt.lib.RecipientRewriteTableContract;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 
-public class JPARecipientRewriteTableTest extends AbstractRecipientRewriteTableTest {
+class JPARecipientRewriteTableTest implements RecipientRewriteTableContract {
 
-    private static final JpaTestCluster JPA_TEST_CLUSTER = JpaTestCluster.create(JPARecipientRewrite.class);
+    static final JpaTestCluster JPA_TEST_CLUSTER = JpaTestCluster.create(JPARecipientRewrite.class);
 
-    @Override
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
+    AbstractRecipientRewriteTable recipientRewriteTable;
+
+    @BeforeEach
+    void setup() throws Exception {
+        setUp();
     }
 
-    @Override
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
+    @AfterEach
+    void teardown() throws Exception {
+        tearDown();
     }
 
     @Override
-    protected AbstractRecipientRewriteTable getRecipientRewriteTable() throws Exception {
+    public void createRecipientRewriteTable() {
         JPARecipientRewriteTable localVirtualUserTable = new JPARecipientRewriteTable();
         localVirtualUserTable.setEntityManagerFactory(JPA_TEST_CLUSTER.getEntityManagerFactory());
-        return localVirtualUserTable;
+        recipientRewriteTable = localVirtualUserTable;
+    }
+
+    @Override
+    public AbstractRecipientRewriteTable virtualUserTable() {
+        return recipientRewriteTable;
     }
 }
diff --git a/server/data/data-library/src/test/java/org/apache/james/rrt/lib/AbstractRecipientRewriteTableTest.java b/server/data/data-library/src/test/java/org/apache/james/rrt/lib/AbstractRecipientRewriteTableTest.java
deleted file mode 100644
index c837815..0000000
--- a/server/data/data-library/src/test/java/org/apache/james/rrt/lib/AbstractRecipientRewriteTableTest.java
+++ /dev/null
@@ -1,643 +0,0 @@
-/****************************************************************
- * 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.rrt.lib;
-
-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 java.util.Map;
-
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.james.core.Domain;
-import org.apache.james.domainlist.api.mock.SimpleDomainList;
-import org.apache.james.lifecycle.api.LifecycleUtil;
-import org.apache.james.rrt.api.RecipientRewriteTable.ErrorMappingException;
-import org.apache.james.rrt.api.RecipientRewriteTableConfiguration;
-import org.apache.james.rrt.api.RecipientRewriteTableException;
-import org.apache.james.rrt.api.SourceDomainIsNotInDomainListException;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import com.github.fge.lambdas.Throwing;
-
-/**
- * The abstract test for the virtual user table. Contains tests related to
- * simple, regexp, wildcard, error,... Extend this and instantiate the needed
- * virtualUserTable implementation.
- */
-public abstract class AbstractRecipientRewriteTableTest {
-
-    private static final String USER = "test";
-    private static final String ADDRESS = "test@localhost2";
-    private static final String ADDRESS_2 = "test@james";
-    private static final Domain SUPPORTED_DOMAIN = Domain.LOCALHOST;
-    private static final MappingSource SOURCE = MappingSource.fromUser(USER, SUPPORTED_DOMAIN);
-    private static final Domain NOT_SUPPORTED_DOMAIN = Domain.of("notAManagedDomain");
-    private static final MappingSource SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST = MappingSource.fromUser(USER, NOT_SUPPORTED_DOMAIN);
-
-    protected abstract AbstractRecipientRewriteTable getRecipientRewriteTable() throws Exception;
-
-    @Rule public ExpectedException expectedException = ExpectedException.none();
-
-    protected AbstractRecipientRewriteTable virtualUserTable;
-
-    public void setUp() throws Exception {
-        setRecursiveRecipientRewriteTable();
-    }
-
-    private void setRecursiveRecipientRewriteTable() throws Exception {
-        setNotConfiguredRecipientRewriteTable();
-        virtualUserTable.setConfiguration(new RecipientRewriteTableConfiguration(true, 10));
-    }
-
-    private void setNonRecursiveRecipientRewriteTable() throws Exception {
-        setNotConfiguredRecipientRewriteTable();
-        virtualUserTable.setConfiguration(new RecipientRewriteTableConfiguration(false, 0));
-    }
-
-    private void setNotConfiguredRecipientRewriteTable() throws Exception {
-        virtualUserTable = getRecipientRewriteTable();
-
-        SimpleDomainList domainList = new SimpleDomainList();
-        domainList.addDomain(SUPPORTED_DOMAIN);
-        virtualUserTable.setDomainList(domainList);
-    }
-
-    public void tearDown() throws Exception {
-        Map<MappingSource, Mappings> mappings = virtualUserTable.getAllMappings();
-
-        if (mappings != null) {
-            for (MappingSource key : virtualUserTable.getAllMappings().keySet()) {
-                Mappings map = mappings.get(key);
-
-                map.asStream()
-                    .forEach(Throwing.consumer(mapping ->
-                        virtualUserTable.removeMapping(key, mapping)));
-            }
-        }
-
-        LifecycleUtil.dispose(virtualUserTable);
-    }
-
-    @Test
-    public void testStoreAndGetMappings() throws Exception {
-        Domain domain = Domain.of("test");
-        virtualUserTable.addMapping(MappingSource.fromDomain(domain), Mapping.regex("prefix_.*:admin@test"));
-        assertThat(virtualUserTable.getResolvedMappings("prefix_abc", domain)).isNotEmpty();
-    }
-
-    @Test
-    public void notConfiguredResolutionShouldThrow() throws Exception {
-        setNotConfiguredRecipientRewriteTable();
-        assertThatCode(() -> virtualUserTable.getResolvedMappings(USER, Domain.LOCALHOST))
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void configuringTwiceShouldThrow() {
-        assertThatCode(() -> virtualUserTable.setConfiguration(new RecipientRewriteTableConfiguration(true, 10)))
-            .isInstanceOf(IllegalStateException.class);
-    }
-
-    @Test
-    public void testStoreAndRetrieveRegexMapping() throws Exception {
-        String regex = "(.*)@localhost";
-        String regex2 = "(.+)@test";
-        String invalidRegex = ".*):";
-
-        Mapping mappingRegex = Mapping.regex(regex);
-        Mapping mappingRegex2 = Mapping.regex(regex2);
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).describedAs("No mapping").isEmpty();
-
-        virtualUserTable.addMapping(SOURCE, mappingRegex);
-        virtualUserTable.addMapping(SOURCE, mappingRegex2);
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).describedAs("Two mappings")
-            .containsOnly(mappingRegex, mappingRegex2);
-        assertThat(virtualUserTable.getAllMappings()).describedAs("One mappingline").hasSize(1);
-        virtualUserTable.removeMapping(SOURCE, mappingRegex);
-
-        assertThatThrownBy(() -> virtualUserTable.addRegexMapping(SOURCE, invalidRegex))
-            .describedAs("Invalid Mapping throw exception")
-            .isInstanceOf(RecipientRewriteTableException.class);
-
-
-        virtualUserTable.removeMapping(SOURCE, mappingRegex2);
-
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).describedAs("No mapping").isEmpty();
-        assertThat(virtualUserTable.getAllMappings()).describedAs("No mapping").isEmpty();
-    }
-
-    @Test
-    public void getAllMappingsShouldListAllEntries() throws Exception {
-        String user2 = "test2";
-        String regex = "(.*)@localhost";
-        String regex2 = "(.+)@test";
-
-        Mapping mappingAddress = Mapping.address(USER + "@" + Domain.LOCALHOST.asString());
-        Mapping mappingRegex = Mapping.regex(regex);
-        Mapping mappingRegex2 = Mapping.regex(regex2);
-        MappingSource source2 = MappingSource.fromUser(user2, Domain.LOCALHOST);
-
-        virtualUserTable.addMapping(SOURCE, mappingRegex);
-        virtualUserTable.addMapping(SOURCE, mappingRegex2);
-        virtualUserTable.addMapping(source2, mappingAddress);
-
-        assertThat(virtualUserTable.getAllMappings())
-            .describedAs("One mappingline")
-            .containsOnly(
-                Pair.of(SOURCE, MappingsImpl.builder()
-                    .add(mappingRegex)
-                    .add(mappingRegex2)
-                    .build()),
-                Pair.of(source2, MappingsImpl.builder()
-                    .add(mappingAddress)
-                    .build()));
-    }
-
-    @Test
-    public void testStoreAndRetrieveAddressMapping() throws Exception {
-        Mapping mappingAddress = Mapping.address(ADDRESS);
-        Mapping mappingAddress2 = Mapping.address(ADDRESS_2);
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).describedAs("No mapping").isEmpty();
-
-        virtualUserTable.addMapping(SOURCE, mappingAddress);
-        virtualUserTable.addMapping(SOURCE, mappingAddress2);
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).describedAs("Two mappings")
-            .containsOnly(mappingAddress, mappingAddress2);
-        assertThat(virtualUserTable.getAllMappings()).describedAs("One mappingline").hasSize(1);
-
-        virtualUserTable.removeMapping(SOURCE, mappingAddress);
-        virtualUserTable.removeMapping(SOURCE, mappingAddress2);
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).describedAs("No mapping").isEmpty();
-        assertThat(virtualUserTable.getAllMappings()).describedAs("No mapping").isEmpty();
-    }
-
-    @Test
-    public void testStoreAndRetrieveErrorMapping() throws Exception {
-        String error = "bounce!";
-
-        assertThat(virtualUserTable.getResolvedMappings(USER, Domain.LOCALHOST)).describedAs("No mapping").isEmpty();
-
-        virtualUserTable.addMapping(SOURCE, Mapping.error(error));
-        assertThat(virtualUserTable.getAllMappings()).describedAs("One mappingline").hasSize(1);
-
-        assertThatThrownBy(() ->
-            virtualUserTable.getResolvedMappings(USER, Domain.LOCALHOST))
-            .describedAs("Exception thrown on to many mappings")
-            .isInstanceOf(ErrorMappingException.class);
-
-        virtualUserTable.removeMapping(SOURCE, Mapping.error(error));
-
-        assertThat(virtualUserTable.getResolvedMappings(USER, Domain.LOCALHOST)).describedAs("No mapping").isEmpty();
-        assertThat(virtualUserTable.getAllMappings()).describedAs("No mapping").isEmpty();
-    }
-
-    @Test
-    public void testStoreAndRetrieveWildCardAddressMapping() throws Exception {
-        String user2 = "test2";
-
-        Mapping mappingAddress = Mapping.address(ADDRESS);
-        Mapping mappingAddress2 = Mapping.address(ADDRESS_2);
-
-        assertThat(virtualUserTable.getResolvedMappings(USER, Domain.LOCALHOST)).describedAs("No mapping").isEmpty();
-
-        virtualUserTable.addMapping(MappingSource.fromDomain(Domain.LOCALHOST), mappingAddress);
-        virtualUserTable.addMapping(SOURCE, mappingAddress2);
-
-        assertThat(virtualUserTable.getResolvedMappings(USER, Domain.LOCALHOST)).describedAs("One mappings")
-            .containsOnly(mappingAddress2);
-        assertThat(virtualUserTable.getResolvedMappings(user2, Domain.LOCALHOST)).describedAs("One mappings")
-            .containsOnly(mappingAddress);
-
-        virtualUserTable.removeMapping(SOURCE, mappingAddress2);
-        virtualUserTable.removeMapping(MappingSource.fromDomain(Domain.LOCALHOST), mappingAddress);
-
-        assertThat(virtualUserTable.getResolvedMappings(USER, Domain.LOCALHOST)).describedAs("No mapping").isEmpty();
-        assertThat(virtualUserTable.getResolvedMappings(user2, Domain.LOCALHOST)).describedAs("No mapping").isEmpty();
-    }
-
-    @Test
-    public void testNonRecursiveMapping() throws Exception {
-        String user1 = "user1";
-        String user2 = "user2";
-        String user3 = "user3";
-        Domain domain1 = Domain.of("domain1");
-        Domain domain2 = Domain.of("domain2");
-        Domain domain3 = Domain.of("domain3");
-        MappingSource source1 = MappingSource.fromUser(user1, domain1);
-        MappingSource source2 = MappingSource.fromUser(user2, domain2);
-
-        setNonRecursiveRecipientRewriteTable();
-
-        assertThat(virtualUserTable.getAllMappings()).describedAs("No mapping").isEmpty();
-
-        virtualUserTable.addMapping(source1, Mapping.address(user2 + "@" + domain2.asString()));
-        virtualUserTable.addMapping(source2, Mapping.address(user3 + "@" + domain3.asString()));
-        assertThatThrownBy(() ->
-            virtualUserTable.getResolvedMappings(user1, domain1))
-            .describedAs("Exception thrown on too many mappings")
-            .isInstanceOf(ErrorMappingException.class);
-    }
-
-    @Test
-    public void testAliasDomainMapping() throws Exception {
-        String domain = "realdomain";
-        Domain aliasDomain = Domain.of("aliasdomain");
-        String user = "user";
-        String user2 = "user2";
-
-        Mapping mappingAddress = Mapping.address(user2 + "@" + domain);
-        Mapping mappingDomain = Mapping.domain(Domain.of(domain));
-
-        assertThat(virtualUserTable.getAllMappings()).describedAs("No mappings").isEmpty();
-
-        virtualUserTable.addMapping(MappingSource.fromDomain(aliasDomain), mappingAddress);
-        virtualUserTable.addMapping(MappingSource.fromDomain(aliasDomain), mappingDomain);
-
-        assertThat(virtualUserTable.getResolvedMappings(user, aliasDomain))
-            .describedAs("Domain mapped as first, Address mapped as second")
-            .isEqualTo(MappingsImpl.builder()
-                .add(Mapping.address(user + "@" + domain))
-                .add(mappingAddress)
-                .build());
-
-        virtualUserTable.removeMapping(MappingSource.fromDomain(aliasDomain), mappingAddress);
-        virtualUserTable.removeMapping(MappingSource.fromDomain(aliasDomain), mappingDomain);
-    }
-
-    @Test
-    public void addMappingShouldThrowWhenMappingAlreadyExists() throws Exception {
-        expectedException.expect(RecipientRewriteTableException.class);
-
-        virtualUserTable.addAddressMapping(SOURCE, ADDRESS);
-        virtualUserTable.addAddressMapping(SOURCE, ADDRESS);
-    }
-
-    @Test
-    public void addMappingShouldNotThrowWhenMappingAlreadyExistsWithAnOtherType() throws Exception {
-        Mapping mappingAddress = Mapping.address(ADDRESS);
-        Mapping mappingRegex = Mapping.regex(ADDRESS);
-
-        virtualUserTable.addMapping(SOURCE, mappingAddress);
-        virtualUserTable.addMapping(SOURCE, mappingRegex);
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).containsOnly(mappingAddress, mappingRegex);
-    }
-
-    @Test
-    public void addForwardMappingShouldStore() throws Exception {
-        Mapping mappingForward = Mapping.forward(ADDRESS);
-        Mapping mappingForward2 = Mapping.forward(ADDRESS_2);
-
-        virtualUserTable.addMapping(SOURCE, mappingForward);
-        virtualUserTable.addMapping(SOURCE, mappingForward2);
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).containsOnly(mappingForward, mappingForward2);
-    }
-
-    @Test
-    public void removeForwardMappingShouldDelete() throws Exception {
-        Mapping mappingForward = Mapping.forward(ADDRESS);
-        Mapping mappingForward2 = Mapping.forward(ADDRESS_2);
-        MappingSource source = MappingSource.fromUser(USER, Domain.LOCALHOST);
-
-        virtualUserTable.addMapping(source, mappingForward);
-        virtualUserTable.addMapping(source, mappingForward2);
-
-        virtualUserTable.removeMapping(source, mappingForward);
-        virtualUserTable.removeMapping(source, mappingForward2);
-
-        assertThat(virtualUserTable.getStoredMappings(source)).isEmpty();
-    }
-
-    @Test
-    public void addGroupMappingShouldStore() throws Exception {
-        Mapping mappingGroup = Mapping.group(ADDRESS);
-        Mapping mappingGroup2 = Mapping.group(ADDRESS_2);
-
-        virtualUserTable.addMapping(SOURCE, mappingGroup);
-        virtualUserTable.addMapping(SOURCE, mappingGroup2);
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).containsOnly(mappingGroup, mappingGroup2);
-    }
-
-    @Test
-    public void removeGroupMappingShouldDelete() throws Exception {
-        Mapping mappingGroup = Mapping.group(ADDRESS);
-        Mapping mappingGroup2 = Mapping.group(ADDRESS_2);
-
-        virtualUserTable.addMapping(SOURCE, mappingGroup);
-        virtualUserTable.addMapping(SOURCE, mappingGroup2);
-
-        virtualUserTable.removeMapping(SOURCE, mappingGroup);
-        virtualUserTable.removeMapping(SOURCE, mappingGroup2);
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).isEmpty();
-    }
-
-    @Test
-    public void addAliasMappingShouldStore() throws Exception {
-        Mapping mappingAlias = Mapping.alias(ADDRESS);
-        Mapping mappingAlias2 = Mapping.alias(ADDRESS_2);
-
-        virtualUserTable.addMapping(SOURCE, mappingAlias);
-        virtualUserTable.addMapping(SOURCE, mappingAlias2);
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).containsOnly(mappingAlias, mappingAlias2);
-    }
-
-    @Test
-    public void removeAliasMappingShouldDelete() throws Exception {
-        Mapping mappingAlias = Mapping.alias(ADDRESS);
-        Mapping mappingAlias2 = Mapping.alias(ADDRESS_2);
-
-        virtualUserTable.addMapping(SOURCE, mappingAlias);
-        virtualUserTable.addMapping(SOURCE, mappingAlias2);
-
-        virtualUserTable.removeMapping(SOURCE, mappingAlias);
-        virtualUserTable.removeMapping(SOURCE, mappingAlias2);
-
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).isEmpty();
-    }
-
-    @Test
-    public void getUserDomainMappingShouldBeEmptyByDefault() throws Exception {
-        assertThat(virtualUserTable.getStoredMappings(SOURCE)).isEmpty();
-    }
-
-    @Test
-    public void listSourcesShouldReturnWhenHasMapping() throws Exception {
-        Mapping mapping = Mapping.group(ADDRESS);
-        virtualUserTable.addMapping(SOURCE, mapping);
-
-        assertThat(virtualUserTable.listSources(mapping)).contains(SOURCE);
-    }
-
-    @Test
-    public void listSourcesShouldReturnWhenMultipleSourceMapping() throws Exception {
-        MappingSource source = MappingSource.fromUser(USER, Domain.of("james"));
-        MappingSource source2 = MappingSource.fromDomain(Domain.LOCALHOST);
-        Mapping mapping = Mapping.group(ADDRESS);
-
-        virtualUserTable.addMapping(source, mapping);
-        virtualUserTable.addMapping(source2, mapping);
-
-        assertThat(virtualUserTable.listSources(mapping)).contains(source, source2);
-    }
-
-    @Test
-    public void listSourcesShouldReturnWhenHasForwardMapping() throws Exception {
-        Mapping mapping = Mapping.forward("forward");
-
-        virtualUserTable.addMapping(SOURCE, mapping);
-
-        assertThat(virtualUserTable.listSources(mapping)).contains(SOURCE);
-    }
-
-    @Test
-    public void listSourcesShouldReturnAliasMappings() throws Exception {
-        Mapping mapping = Mapping.alias("alias");
-
-        virtualUserTable.addMapping(SOURCE, mapping);
-
-        assertThat(virtualUserTable.listSources(mapping)).contains(SOURCE);
-    }
-
-    @Test
-    public void listSourcesShouldReturnWhenHasAddressMapping() throws Exception {
-        Mapping mapping = Mapping.address("address");
-
-        virtualUserTable.addMapping(SOURCE, mapping);
-
-        assertThat(virtualUserTable.listSources(mapping)).contains(SOURCE);
-    }
-
-    @Test
-    public void listSourcesShouldThrowExceptionWhenHasRegexMapping() throws Exception {
-        Mapping mapping = Mapping.regex("regex");
-
-        virtualUserTable.addMapping(SOURCE, mapping);
-
-        assertThatThrownBy(() -> virtualUserTable.listSources(mapping))
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void listSourcesShouldHandleDomainMapping() throws Exception {
-        Mapping mapping = Mapping.domain(Domain.of("domain"));
-
-        virtualUserTable.addMapping(SOURCE, mapping);
-
-        assertThat(virtualUserTable.listSources(mapping))
-            .containsExactly(SOURCE);
-    }
-
-    @Test
-    public void listSourcesShouldReturnEmptyWhenNoDomainAlias() throws Exception {
-        Mapping mapping = Mapping.domain(Domain.of("domain"));
-
-        assertThat(virtualUserTable.listSources(mapping)).isEmpty();
-    }
-
-    @Test
-    public void listSourcesShouldHandleDomainSource() throws Exception {
-        Mapping mapping = Mapping.domain(Domain.of("domain"));
-
-        MappingSource source = MappingSource.fromDomain(Domain.of("source.org"));
-        virtualUserTable.addMapping(source, mapping);
-
-        assertThat(virtualUserTable.listSources(mapping))
-            .containsExactly(source);
-    }
-
-    @Test
-    public void listSourcesShouldHandleDomainSources() throws Exception {
-        Mapping mapping = Mapping.domain(Domain.of("domain"));
-
-        MappingSource source1 = MappingSource.fromDomain(Domain.of("source1.org"));
-        MappingSource source2 = MappingSource.fromDomain(Domain.of("source2.org"));
-        virtualUserTable.addMapping(source1, mapping);
-        virtualUserTable.addMapping(source2, mapping);
-
-        assertThat(virtualUserTable.listSources(mapping))
-            .containsExactlyInAnyOrder(source1, source2);
-    }
-
-    @Test
-    public void listSourcesShouldThrowExceptionWhenHasErrorMapping() throws Exception {
-        Mapping mapping = Mapping.error("error");
-
-        virtualUserTable.addMapping(SOURCE, mapping);
-
-        assertThatThrownBy(() -> virtualUserTable.listSources(mapping))
-            .isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void listSourcesShouldReturnEmptyWhenMappingDoesNotExist() throws Exception {
-        Mapping domainMapping = Mapping.domain(Domain.of("domain"));
-        Mapping groupMapping = Mapping.group("group");
-
-        virtualUserTable.addMapping(SOURCE, domainMapping);
-
-        assertThat(virtualUserTable.listSources(groupMapping)).isEmpty();
-    }
-
-    @Test
-    public void getSourcesForTypeShouldReturnEmptyWhenNoMapping() throws Exception {
-        assertThat(virtualUserTable.getSourcesForType(Mapping.Type.Alias)).isEmpty();
-    }
-
-    @Test
-    public void getSourcesForTypeShouldReturnEmptyWhenNoMatchingMapping() throws Exception {
-        virtualUserTable.addForwardMapping(SOURCE, ADDRESS);
-
-        assertThat(virtualUserTable.getSourcesForType(Mapping.Type.Alias)).isEmpty();
-    }
-
-    @Test
-    public void getSourcesForTypeShouldReturnMatchingMapping() throws Exception {
-        virtualUserTable.addAliasMapping(SOURCE, ADDRESS);
-
-        assertThat(virtualUserTable.getSourcesForType(Mapping.Type.Alias)).containsOnly(SOURCE);
-    }
-
-    @Test
-    public void getSourcesForTypeShouldNotReturnDuplicatedSources() throws Exception {
-        virtualUserTable.addAliasMapping(SOURCE, ADDRESS);
-        virtualUserTable.addAliasMapping(SOURCE, ADDRESS_2);
-
-        assertThat(virtualUserTable.getSourcesForType(Mapping.Type.Alias)).containsExactly(SOURCE);
-    }
-
-    @Test
-    public void getSourcesForTypeShouldReturnSortedStream() throws Exception {
-        MappingSource source1 = MappingSource.fromUser("alice", Domain.LOCALHOST);
-        MappingSource source2 = MappingSource.fromUser("bob", Domain.LOCALHOST);
-        MappingSource source3 = MappingSource.fromUser("cedric", Domain.LOCALHOST);
-
-        virtualUserTable.addAliasMapping(source1, ADDRESS);
-        virtualUserTable.addAliasMapping(source3, ADDRESS);
-        virtualUserTable.addAliasMapping(source2, ADDRESS);
-
-        assertThat(virtualUserTable.getSourcesForType(Mapping.Type.Alias))
-            .containsExactly(source1, source2, source3);
-    }
-
-    @Test
-    public void getMappingsForTypeShouldReturnEmptyWhenNoMapping() throws Exception {
-        assertThat(virtualUserTable.getMappingsForType(Mapping.Type.Alias)).isEmpty();
-    }
-
-    @Test
-    public void getMappingsForTypeShouldReturnEmptyWhenNoMatchingMapping() throws Exception {
-        virtualUserTable.addForwardMapping(SOURCE, ADDRESS);
-
-        assertThat(virtualUserTable.getMappingsForType(Mapping.Type.Alias)).isEmpty();
-    }
-
-    @Test
-    public void getMappingsForTypeShouldReturnMatchingMapping() throws Exception {
-        virtualUserTable.addAliasMapping(SOURCE, ADDRESS);
-
-        assertThat(virtualUserTable.getMappingsForType(Mapping.Type.Alias)).containsOnly(Mapping.alias(ADDRESS));
-    }
-
-    @Test
-    public void getMappingsForTypeShouldNotReturnDuplicatedDestinations() throws Exception {
-        MappingSource source2 = MappingSource.fromUser("bob", Domain.LOCALHOST);
-
-        virtualUserTable.addAliasMapping(SOURCE, ADDRESS);
-        virtualUserTable.addAliasMapping(source2, ADDRESS);
-
-        assertThat(virtualUserTable.getMappingsForType(Mapping.Type.Alias)).containsExactly(Mapping.alias(ADDRESS));
-    }
-
-    @Test
-    public void getMappingsForTypeShouldReturnSortedStream() throws Exception {
-        String address1 = "alice@domain.com";
-        String address2 = "bob@domain.com";
-        String address3 = "cedric@domain.com";
-        Mapping mapping1 = Mapping.alias(address1);
-        Mapping mapping2 = Mapping.alias(address2);
-        Mapping mapping3 = Mapping.alias(address3);
-
-        virtualUserTable.addAliasMapping(SOURCE, address1);
-        virtualUserTable.addAliasMapping(SOURCE, address3);
-        virtualUserTable.addAliasMapping(SOURCE, address2);
-
-        assertThat(virtualUserTable.getMappingsForType(Mapping.Type.Alias))
-            .containsExactly(mapping1, mapping2, mapping3);
-    }
-
-    @Test
-    public void addRegexMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
-        assertThatThrownBy(() -> virtualUserTable.addRegexMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, ".*@localhost"))
-            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
-    }
-
-    @Test
-    public void addAddressMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
-        assertThatThrownBy(() -> virtualUserTable.addAddressMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, ADDRESS))
-            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
-    }
-
-    @Test
-    public void addErrorMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
-        assertThatThrownBy(() -> virtualUserTable.addErrorMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, "error"))
-            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
-    }
-
-    @Test
-    public void addDomainMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
-        assertThatThrownBy(() -> virtualUserTable.addDomainMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, SUPPORTED_DOMAIN))
-            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
-    }
-
-    @Test
-    public void addDomainAliasShouldThrowWhenSourceDomainIsNotInDomainList() {
-        assertThatThrownBy(() -> virtualUserTable.addDomainAliasMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, SUPPORTED_DOMAIN))
-            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
-    }
-
-    @Test
-    public void addForwardMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
-        assertThatThrownBy(() -> virtualUserTable.addForwardMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, ADDRESS))
-            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
-    }
-
-    @Test
-    public void addGroupMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
-        assertThatThrownBy(() -> virtualUserTable.addGroupMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, ADDRESS))
-            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
-    }
-
-    @Test
-    public void addAliasMappingShouldThrowWhenDomainIsNotInDomainList() {
-        assertThatThrownBy(() -> virtualUserTable.addAliasMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, ADDRESS))
-            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
-    }
-}
diff --git a/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java b/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java
new file mode 100644
index 0000000..6d41abd
--- /dev/null
+++ b/server/data/data-library/src/test/java/org/apache/james/rrt/lib/RecipientRewriteTableContract.java
@@ -0,0 +1,636 @@
+/****************************************************************
+ * 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.rrt.lib;
+
+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 java.util.Map;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.james.core.Domain;
+import org.apache.james.domainlist.api.mock.SimpleDomainList;
+import org.apache.james.lifecycle.api.LifecycleUtil;
+import org.apache.james.rrt.api.RecipientRewriteTable.ErrorMappingException;
+import org.apache.james.rrt.api.RecipientRewriteTableConfiguration;
+import org.apache.james.rrt.api.RecipientRewriteTableException;
+import org.apache.james.rrt.api.SourceDomainIsNotInDomainListException;
+import org.junit.jupiter.api.Test;
+
+import com.github.fge.lambdas.Throwing;
+
+/**
+ * The abstract test for the virtual user table. Contains tests related to
+ * simple, regexp, wildcard, error,... Extend this and instantiate the needed
+ * virtualUserTable implementation.
+ */
+public interface RecipientRewriteTableContract {
+
+    String USER = "test";
+    String ADDRESS = "test@localhost2";
+    String ADDRESS_2 = "test@james";
+    Domain SUPPORTED_DOMAIN = Domain.LOCALHOST;
+    MappingSource SOURCE = MappingSource.fromUser(USER, SUPPORTED_DOMAIN);
+    Domain NOT_SUPPORTED_DOMAIN = Domain.of("notAManagedDomain");
+    MappingSource SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST = MappingSource.fromUser(USER, NOT_SUPPORTED_DOMAIN);
+
+    void createRecipientRewriteTable();
+    AbstractRecipientRewriteTable virtualUserTable();
+
+    default void setUp() throws Exception {
+        setRecursiveRecipientRewriteTable();
+    }
+
+    private void setRecursiveRecipientRewriteTable() throws Exception {
+        setNotConfiguredRecipientRewriteTable();
+        virtualUserTable().setConfiguration(new RecipientRewriteTableConfiguration(true, 10));
+    }
+
+    private void setNonRecursiveRecipientRewriteTable() throws Exception {
+        setNotConfiguredRecipientRewriteTable();
+        virtualUserTable().setConfiguration(new RecipientRewriteTableConfiguration(false, 0));
+    }
+
+    private void setNotConfiguredRecipientRewriteTable() throws Exception {
+        createRecipientRewriteTable();
+
+        SimpleDomainList domainList = new SimpleDomainList();
+        domainList.addDomain(SUPPORTED_DOMAIN);
+        virtualUserTable().setDomainList(domainList);
+    }
+
+    default void tearDown() throws Exception {
+        Map<MappingSource, Mappings> mappings = virtualUserTable().getAllMappings();
+
+        if (mappings != null) {
+            for (MappingSource key : virtualUserTable().getAllMappings().keySet()) {
+                Mappings map = mappings.get(key);
+
+                map.asStream()
+                    .forEach(Throwing.consumer(mapping ->
+                        virtualUserTable().removeMapping(key, mapping)));
+            }
+        }
+
+        LifecycleUtil.dispose(virtualUserTable());
+    }
+
+    @Test
+    default void testStoreAndGetMappings() throws Exception {
+        Domain domain = Domain.of("test");
+        virtualUserTable().addMapping(MappingSource.fromDomain(domain), Mapping.regex("prefix_.*:admin@test"));
+        assertThat(virtualUserTable().getResolvedMappings("prefix_abc", domain)).isNotEmpty();
+    }
+
+    @Test
+    default void notConfiguredResolutionShouldThrow() throws Exception {
+        setNotConfiguredRecipientRewriteTable();
+        assertThatCode(() -> virtualUserTable().getResolvedMappings(USER, Domain.LOCALHOST))
+            .isInstanceOf(IllegalStateException.class);
+    }
+
+    @Test
+    default void configuringTwiceShouldThrow() {
+        assertThatCode(() -> virtualUserTable().setConfiguration(new RecipientRewriteTableConfiguration(true, 10)))
+            .isInstanceOf(IllegalStateException.class);
+    }
+
+    @Test
+    default void testStoreAndRetrieveRegexMapping() throws Exception {
+        String regex = "(.*)@localhost";
+        String regex2 = "(.+)@test";
+        String invalidRegex = ".*):";
+
+        Mapping mappingRegex = Mapping.regex(regex);
+        Mapping mappingRegex2 = Mapping.regex(regex2);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).describedAs("No mapping").isEmpty();
+
+        virtualUserTable().addMapping(SOURCE, mappingRegex);
+        virtualUserTable().addMapping(SOURCE, mappingRegex2);
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).describedAs("Two mappings")
+            .containsOnly(mappingRegex, mappingRegex2);
+        assertThat(virtualUserTable().getAllMappings()).describedAs("One mappingline").hasSize(1);
+        virtualUserTable().removeMapping(SOURCE, mappingRegex);
+
+        assertThatThrownBy(() -> virtualUserTable().addRegexMapping(SOURCE, invalidRegex))
+            .describedAs("Invalid Mapping throw exception")
+            .isInstanceOf(RecipientRewriteTableException.class);
+        
+        virtualUserTable().removeMapping(SOURCE, mappingRegex2);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).describedAs("No mapping").isEmpty();
+        assertThat(virtualUserTable().getAllMappings()).describedAs("No mapping").isEmpty();
+    }
+
+    @Test
+    default void getAllMappingsShouldListAllEntries() throws Exception {
+        String user2 = "test2";
+        String regex = "(.*)@localhost";
+        String regex2 = "(.+)@test";
+
+        Mapping mappingAddress = Mapping.address(USER + "@" + Domain.LOCALHOST.asString());
+        Mapping mappingRegex = Mapping.regex(regex);
+        Mapping mappingRegex2 = Mapping.regex(regex2);
+        MappingSource source2 = MappingSource.fromUser(user2, Domain.LOCALHOST);
+
+        virtualUserTable().addMapping(SOURCE, mappingRegex);
+        virtualUserTable().addMapping(SOURCE, mappingRegex2);
+        virtualUserTable().addMapping(source2, mappingAddress);
+
+        assertThat(virtualUserTable().getAllMappings())
+            .describedAs("One mappingline")
+            .containsOnly(
+                Pair.of(SOURCE, MappingsImpl.builder()
+                    .add(mappingRegex)
+                    .add(mappingRegex2)
+                    .build()),
+                Pair.of(source2, MappingsImpl.builder()
+                    .add(mappingAddress)
+                    .build()));
+    }
+
+    @Test
+    default void testStoreAndRetrieveAddressMapping() throws Exception {
+        Mapping mappingAddress = Mapping.address(ADDRESS);
+        Mapping mappingAddress2 = Mapping.address(ADDRESS_2);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).describedAs("No mapping").isEmpty();
+
+        virtualUserTable().addMapping(SOURCE, mappingAddress);
+        virtualUserTable().addMapping(SOURCE, mappingAddress2);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).describedAs("Two mappings")
+            .containsOnly(mappingAddress, mappingAddress2);
+        assertThat(virtualUserTable().getAllMappings()).describedAs("One mappingline").hasSize(1);
+
+        virtualUserTable().removeMapping(SOURCE, mappingAddress);
+        virtualUserTable().removeMapping(SOURCE, mappingAddress2);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).describedAs("No mapping").isEmpty();
+        assertThat(virtualUserTable().getAllMappings()).describedAs("No mapping").isEmpty();
+    }
+
+    @Test
+    default void testStoreAndRetrieveErrorMapping() throws Exception {
+        String error = "bounce!";
+
+        assertThat(virtualUserTable().getResolvedMappings(USER, Domain.LOCALHOST)).describedAs("No mapping").isEmpty();
+
+        virtualUserTable().addMapping(SOURCE, Mapping.error(error));
+        assertThat(virtualUserTable().getAllMappings()).describedAs("One mappingline").hasSize(1);
+
+        assertThatThrownBy(() ->
+            virtualUserTable().getResolvedMappings(USER, Domain.LOCALHOST))
+            .describedAs("Exception thrown on to many mappings")
+            .isInstanceOf(ErrorMappingException.class);
+
+        virtualUserTable().removeMapping(SOURCE, Mapping.error(error));
+
+        assertThat(virtualUserTable().getResolvedMappings(USER, Domain.LOCALHOST)).describedAs("No mapping").isEmpty();
+        assertThat(virtualUserTable().getAllMappings()).describedAs("No mapping").isEmpty();
+    }
+
+    @Test
+    default void testStoreAndRetrieveWildCardAddressMapping() throws Exception {
+        String user2 = "test2";
+
+        Mapping mappingAddress = Mapping.address(ADDRESS);
+        Mapping mappingAddress2 = Mapping.address(ADDRESS_2);
+
+        assertThat(virtualUserTable().getResolvedMappings(USER, Domain.LOCALHOST)).describedAs("No mapping").isEmpty();
+
+        virtualUserTable().addMapping(MappingSource.fromDomain(Domain.LOCALHOST), mappingAddress);
+        virtualUserTable().addMapping(SOURCE, mappingAddress2);
+
+        assertThat(virtualUserTable().getResolvedMappings(USER, Domain.LOCALHOST)).describedAs("One mappings")
+            .containsOnly(mappingAddress2);
+        assertThat(virtualUserTable().getResolvedMappings(user2, Domain.LOCALHOST)).describedAs("One mappings")
+            .containsOnly(mappingAddress);
+
+        virtualUserTable().removeMapping(SOURCE, mappingAddress2);
+        virtualUserTable().removeMapping(MappingSource.fromDomain(Domain.LOCALHOST), mappingAddress);
+
+        assertThat(virtualUserTable().getResolvedMappings(USER, Domain.LOCALHOST)).describedAs("No mapping").isEmpty();
+        assertThat(virtualUserTable().getResolvedMappings(user2, Domain.LOCALHOST)).describedAs("No mapping").isEmpty();
+    }
+
+    @Test
+    default void testNonRecursiveMapping() throws Exception {
+        String user1 = "user1";
+        String user2 = "user2";
+        String user3 = "user3";
+        Domain domain1 = Domain.of("domain1");
+        Domain domain2 = Domain.of("domain2");
+        Domain domain3 = Domain.of("domain3");
+        MappingSource source1 = MappingSource.fromUser(user1, domain1);
+        MappingSource source2 = MappingSource.fromUser(user2, domain2);
+
+        setNonRecursiveRecipientRewriteTable();
+
+        assertThat(virtualUserTable().getAllMappings()).describedAs("No mapping").isEmpty();
+
+        virtualUserTable().addMapping(source1, Mapping.address(user2 + "@" + domain2.asString()));
+        virtualUserTable().addMapping(source2, Mapping.address(user3 + "@" + domain3.asString()));
+        assertThatThrownBy(() ->
+            virtualUserTable().getResolvedMappings(user1, domain1))
+            .describedAs("Exception thrown on too many mappings")
+            .isInstanceOf(ErrorMappingException.class);
+    }
+
+    @Test
+    default void testAliasDomainMapping() throws Exception {
+        String domain = "realdomain";
+        Domain aliasDomain = Domain.of("aliasdomain");
+        String user = "user";
+        String user2 = "user2";
+
+        Mapping mappingAddress = Mapping.address(user2 + "@" + domain);
+        Mapping mappingDomain = Mapping.domain(Domain.of(domain));
+
+        assertThat(virtualUserTable().getAllMappings()).describedAs("No mappings").isEmpty();
+
+        virtualUserTable().addMapping(MappingSource.fromDomain(aliasDomain), mappingAddress);
+        virtualUserTable().addMapping(MappingSource.fromDomain(aliasDomain), mappingDomain);
+
+        assertThat(virtualUserTable().getResolvedMappings(user, aliasDomain))
+            .describedAs("Domain mapped as first, Address mapped as second")
+            .isEqualTo(MappingsImpl.builder()
+                .add(Mapping.address(user + "@" + domain))
+                .add(mappingAddress)
+                .build());
+
+        virtualUserTable().removeMapping(MappingSource.fromDomain(aliasDomain), mappingAddress);
+        virtualUserTable().removeMapping(MappingSource.fromDomain(aliasDomain), mappingDomain);
+    }
+
+    @Test
+    default void addMappingShouldThrowWhenMappingAlreadyExists() throws Exception {
+        virtualUserTable().addAddressMapping(SOURCE, ADDRESS);
+
+        assertThatThrownBy(() -> virtualUserTable().addAddressMapping(SOURCE, ADDRESS))
+            .isInstanceOf(RecipientRewriteTableException.class);
+    }
+
+    @Test
+    default void addMappingShouldNotThrowWhenMappingAlreadyExistsWithAnOtherType() throws Exception {
+        Mapping mappingAddress = Mapping.address(ADDRESS);
+        Mapping mappingRegex = Mapping.regex(ADDRESS);
+
+        virtualUserTable().addMapping(SOURCE, mappingAddress);
+        virtualUserTable().addMapping(SOURCE, mappingRegex);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).containsOnly(mappingAddress, mappingRegex);
+    }
+
+    @Test
+    default void addForwardMappingShouldStore() throws Exception {
+        Mapping mappingForward = Mapping.forward(ADDRESS);
+        Mapping mappingForward2 = Mapping.forward(ADDRESS_2);
+
+        virtualUserTable().addMapping(SOURCE, mappingForward);
+        virtualUserTable().addMapping(SOURCE, mappingForward2);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).containsOnly(mappingForward, mappingForward2);
+    }
+
+    @Test
+    default void removeForwardMappingShouldDelete() throws Exception {
+        Mapping mappingForward = Mapping.forward(ADDRESS);
+        Mapping mappingForward2 = Mapping.forward(ADDRESS_2);
+        MappingSource source = MappingSource.fromUser(USER, Domain.LOCALHOST);
+
+        virtualUserTable().addMapping(source, mappingForward);
+        virtualUserTable().addMapping(source, mappingForward2);
+
+        virtualUserTable().removeMapping(source, mappingForward);
+        virtualUserTable().removeMapping(source, mappingForward2);
+
+        assertThat(virtualUserTable().getStoredMappings(source)).isEmpty();
+    }
+
+    @Test
+    default void addGroupMappingShouldStore() throws Exception {
+        Mapping mappingGroup = Mapping.group(ADDRESS);
+        Mapping mappingGroup2 = Mapping.group(ADDRESS_2);
+
+        virtualUserTable().addMapping(SOURCE, mappingGroup);
+        virtualUserTable().addMapping(SOURCE, mappingGroup2);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).containsOnly(mappingGroup, mappingGroup2);
+    }
+
+    @Test
+    default void removeGroupMappingShouldDelete() throws Exception {
+        Mapping mappingGroup = Mapping.group(ADDRESS);
+        Mapping mappingGroup2 = Mapping.group(ADDRESS_2);
+
+        virtualUserTable().addMapping(SOURCE, mappingGroup);
+        virtualUserTable().addMapping(SOURCE, mappingGroup2);
+
+        virtualUserTable().removeMapping(SOURCE, mappingGroup);
+        virtualUserTable().removeMapping(SOURCE, mappingGroup2);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).isEmpty();
+    }
+
+    @Test
+    default void addAliasMappingShouldStore() throws Exception {
+        Mapping mappingAlias = Mapping.alias(ADDRESS);
+        Mapping mappingAlias2 = Mapping.alias(ADDRESS_2);
+
+        virtualUserTable().addMapping(SOURCE, mappingAlias);
+        virtualUserTable().addMapping(SOURCE, mappingAlias2);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).containsOnly(mappingAlias, mappingAlias2);
+    }
+
+    @Test
+    default void removeAliasMappingShouldDelete() throws Exception {
+        Mapping mappingAlias = Mapping.alias(ADDRESS);
+        Mapping mappingAlias2 = Mapping.alias(ADDRESS_2);
+
+        virtualUserTable().addMapping(SOURCE, mappingAlias);
+        virtualUserTable().addMapping(SOURCE, mappingAlias2);
+
+        virtualUserTable().removeMapping(SOURCE, mappingAlias);
+        virtualUserTable().removeMapping(SOURCE, mappingAlias2);
+
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).isEmpty();
+    }
+
+    @Test
+    default void getUserDomainMappingShouldBeEmptyByDefault() throws Exception {
+        assertThat(virtualUserTable().getStoredMappings(SOURCE)).isEmpty();
+    }
+
+    @Test
+    default void listSourcesShouldReturnWhenHasMapping() throws Exception {
+        Mapping mapping = Mapping.group(ADDRESS);
+        virtualUserTable().addMapping(SOURCE, mapping);
+
+        assertThat(virtualUserTable().listSources(mapping)).contains(SOURCE);
+    }
+
+    @Test
+    default void listSourcesShouldReturnWhenMultipleSourceMapping() throws Exception {
+        MappingSource source = MappingSource.fromUser(USER, Domain.of("james"));
+        MappingSource source2 = MappingSource.fromDomain(Domain.LOCALHOST);
+        Mapping mapping = Mapping.group(ADDRESS);
+
+        virtualUserTable().addMapping(source, mapping);
+        virtualUserTable().addMapping(source2, mapping);
+
+        assertThat(virtualUserTable().listSources(mapping)).contains(source, source2);
+    }
+
+    @Test
+    default void listSourcesShouldReturnWhenHasForwardMapping() throws Exception {
+        Mapping mapping = Mapping.forward("forward");
+
+        virtualUserTable().addMapping(SOURCE, mapping);
+
+        assertThat(virtualUserTable().listSources(mapping)).contains(SOURCE);
+    }
+
+    @Test
+    default void listSourcesShouldReturnAliasMappings() throws Exception {
+        Mapping mapping = Mapping.alias("alias");
+
+        virtualUserTable().addMapping(SOURCE, mapping);
+
+        assertThat(virtualUserTable().listSources(mapping)).contains(SOURCE);
+    }
+
+    @Test
+    default void listSourcesShouldReturnWhenHasAddressMapping() throws Exception {
+        Mapping mapping = Mapping.address("address");
+
+        virtualUserTable().addMapping(SOURCE, mapping);
+
+        assertThat(virtualUserTable().listSources(mapping)).contains(SOURCE);
+    }
+
+    @Test
+    default void listSourcesShouldThrowExceptionWhenHasRegexMapping() throws Exception {
+        Mapping mapping = Mapping.regex("regex");
+
+        virtualUserTable().addMapping(SOURCE, mapping);
+
+        assertThatThrownBy(() -> virtualUserTable().listSources(mapping))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    default void listSourcesShouldHandleDomainMapping() throws Exception {
+        Mapping mapping = Mapping.domain(Domain.of("domain"));
+
+        virtualUserTable().addMapping(SOURCE, mapping);
+
+        assertThat(virtualUserTable().listSources(mapping))
+            .containsExactly(SOURCE);
+    }
+
+    @Test
+    default void listSourcesShouldReturnEmptyWhenNoDomainAlias() throws Exception {
+        Mapping mapping = Mapping.domain(Domain.of("domain"));
+
+        assertThat(virtualUserTable().listSources(mapping)).isEmpty();
+    }
+
+    @Test
+    default void listSourcesShouldHandleDomainSource() throws Exception {
+        Mapping mapping = Mapping.domain(Domain.of("domain"));
+
+        MappingSource source = MappingSource.fromDomain(Domain.of("source.org"));
+        virtualUserTable().addMapping(source, mapping);
+
+        assertThat(virtualUserTable().listSources(mapping))
+            .containsExactly(source);
+    }
+
+    @Test
+    default void listSourcesShouldHandleDomainSources() throws Exception {
+        Mapping mapping = Mapping.domain(Domain.of("domain"));
+
+        MappingSource source1 = MappingSource.fromDomain(Domain.of("source1.org"));
+        MappingSource source2 = MappingSource.fromDomain(Domain.of("source2.org"));
+        virtualUserTable().addMapping(source1, mapping);
+        virtualUserTable().addMapping(source2, mapping);
+
+        assertThat(virtualUserTable().listSources(mapping))
+            .containsExactlyInAnyOrder(source1, source2);
+    }
+
+    @Test
+    default void listSourcesShouldThrowExceptionWhenHasErrorMapping() throws Exception {
+        Mapping mapping = Mapping.error("error");
+
+        virtualUserTable().addMapping(SOURCE, mapping);
+
+        assertThatThrownBy(() -> virtualUserTable().listSources(mapping))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    default void listSourcesShouldReturnEmptyWhenMappingDoesNotExist() throws Exception {
+        Mapping domainMapping = Mapping.domain(Domain.of("domain"));
+        Mapping groupMapping = Mapping.group("group");
+
+        virtualUserTable().addMapping(SOURCE, domainMapping);
+
+        assertThat(virtualUserTable().listSources(groupMapping)).isEmpty();
+    }
+
+    @Test
+    default void getSourcesForTypeShouldReturnEmptyWhenNoMapping() throws Exception {
+        assertThat(virtualUserTable().getSourcesForType(Mapping.Type.Alias)).isEmpty();
+    }
+
+    @Test
+    default void getSourcesForTypeShouldReturnEmptyWhenNoMatchingMapping() throws Exception {
+        virtualUserTable().addForwardMapping(SOURCE, ADDRESS);
+
+        assertThat(virtualUserTable().getSourcesForType(Mapping.Type.Alias)).isEmpty();
+    }
+
+    @Test
+    default void getSourcesForTypeShouldReturnMatchingMapping() throws Exception {
+        virtualUserTable().addAliasMapping(SOURCE, ADDRESS);
+
+        assertThat(virtualUserTable().getSourcesForType(Mapping.Type.Alias)).containsOnly(SOURCE);
+    }
+
+    @Test
+    default void getSourcesForTypeShouldNotReturnDuplicatedSources() throws Exception {
+        virtualUserTable().addAliasMapping(SOURCE, ADDRESS);
+        virtualUserTable().addAliasMapping(SOURCE, ADDRESS_2);
+
+        assertThat(virtualUserTable().getSourcesForType(Mapping.Type.Alias)).containsExactly(SOURCE);
+    }
+
+    @Test
+    default void getSourcesForTypeShouldReturnSortedStream() throws Exception {
+        MappingSource source1 = MappingSource.fromUser("alice", Domain.LOCALHOST);
+        MappingSource source2 = MappingSource.fromUser("bob", Domain.LOCALHOST);
+        MappingSource source3 = MappingSource.fromUser("cedric", Domain.LOCALHOST);
+
+        virtualUserTable().addAliasMapping(source1, ADDRESS);
+        virtualUserTable().addAliasMapping(source3, ADDRESS);
+        virtualUserTable().addAliasMapping(source2, ADDRESS);
+
+        assertThat(virtualUserTable().getSourcesForType(Mapping.Type.Alias))
+            .containsExactly(source1, source2, source3);
+    }
+
+    @Test
+    default void getMappingsForTypeShouldReturnEmptyWhenNoMapping() throws Exception {
+        assertThat(virtualUserTable().getMappingsForType(Mapping.Type.Alias)).isEmpty();
+    }
+
+    @Test
+    default void getMappingsForTypeShouldReturnEmptyWhenNoMatchingMapping() throws Exception {
+        virtualUserTable().addForwardMapping(SOURCE, ADDRESS);
+
+        assertThat(virtualUserTable().getMappingsForType(Mapping.Type.Alias)).isEmpty();
+    }
+
+    @Test
+    default void getMappingsForTypeShouldReturnMatchingMapping() throws Exception {
+        virtualUserTable().addAliasMapping(SOURCE, ADDRESS);
+
+        assertThat(virtualUserTable().getMappingsForType(Mapping.Type.Alias)).containsOnly(Mapping.alias(ADDRESS));
+    }
+
+    @Test
+    default void getMappingsForTypeShouldNotReturnDuplicatedDestinations() throws Exception {
+        MappingSource source2 = MappingSource.fromUser("bob", Domain.LOCALHOST);
+
+        virtualUserTable().addAliasMapping(SOURCE, ADDRESS);
+        virtualUserTable().addAliasMapping(source2, ADDRESS);
+
+        assertThat(virtualUserTable().getMappingsForType(Mapping.Type.Alias)).containsExactly(Mapping.alias(ADDRESS));
+    }
+
+    @Test
+    default void getMappingsForTypeShouldReturnSortedStream() throws Exception {
+        String address1 = "alice@domain.com";
+        String address2 = "bob@domain.com";
+        String address3 = "cedric@domain.com";
+        Mapping mapping1 = Mapping.alias(address1);
+        Mapping mapping2 = Mapping.alias(address2);
+        Mapping mapping3 = Mapping.alias(address3);
+
+        virtualUserTable().addAliasMapping(SOURCE, address1);
+        virtualUserTable().addAliasMapping(SOURCE, address3);
+        virtualUserTable().addAliasMapping(SOURCE, address2);
+
+        assertThat(virtualUserTable().getMappingsForType(Mapping.Type.Alias))
+            .containsExactly(mapping1, mapping2, mapping3);
+    }
+
+    @Test
+    default void addRegexMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
+        assertThatThrownBy(() -> virtualUserTable().addRegexMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, ".*@localhost"))
+            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
+    }
+
+    @Test
+    default void addAddressMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
+        assertThatThrownBy(() -> virtualUserTable().addAddressMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, ADDRESS))
+            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
+    }
+
+    @Test
+    default void addErrorMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
+        assertThatThrownBy(() -> virtualUserTable().addErrorMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, "error"))
+            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
+    }
+
+    @Test
+    default void addDomainMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
+        assertThatThrownBy(() -> virtualUserTable().addDomainMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, SUPPORTED_DOMAIN))
+            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
+    }
+
+    @Test
+    default void addDomainAliasShouldThrowWhenSourceDomainIsNotInDomainList() {
+        assertThatThrownBy(() -> virtualUserTable().addDomainAliasMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, SUPPORTED_DOMAIN))
+            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
+    }
+
+    @Test
+    default void addForwardMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
+        assertThatThrownBy(() -> virtualUserTable().addForwardMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, ADDRESS))
+            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
+    }
+
+    @Test
+    default void addGroupMappingShouldThrowWhenSourceDomainIsNotInDomainList() {
+        assertThatThrownBy(() -> virtualUserTable().addGroupMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, ADDRESS))
+            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
+    }
+
+    @Test
+    default void addAliasMappingShouldThrowWhenDomainIsNotInDomainList() {
+        assertThatThrownBy(() -> virtualUserTable().addAliasMapping(SOURCE_WITH_DOMAIN_NOT_IN_DOMAIN_LIST, ADDRESS))
+            .isInstanceOf(SourceDomainIsNotInDomainListException.class);
+    }
+}
diff --git a/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/MemoryRecipientRewriteTableTest.java b/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/MemoryRecipientRewriteTableTest.java
index 7725797..76aff5e 100644
--- a/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/MemoryRecipientRewriteTableTest.java
+++ b/server/data/data-memory/src/test/java/org/apache/james/rrt/memory/MemoryRecipientRewriteTableTest.java
@@ -20,26 +20,31 @@
 package org.apache.james.rrt.memory;
 
 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
-import org.apache.james.rrt.lib.AbstractRecipientRewriteTableTest;
-import org.junit.After;
-import org.junit.Before;
+import org.apache.james.rrt.lib.RecipientRewriteTableContract;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 
-public class MemoryRecipientRewriteTableTest extends AbstractRecipientRewriteTableTest {
+class MemoryRecipientRewriteTableTest implements RecipientRewriteTableContract {
 
-    @Override
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
+    AbstractRecipientRewriteTable recipientRewriteTable;
+
+    @BeforeEach
+    void setup() throws Exception {
+        setUp();
+    }
+
+    @AfterEach
+    void teardown() throws Exception {
+        tearDown();
     }
 
     @Override
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
+    public void createRecipientRewriteTable() {
+        recipientRewriteTable = new MemoryRecipientRewriteTable();
     }
 
     @Override
-    protected AbstractRecipientRewriteTable getRecipientRewriteTable() {
-        return new MemoryRecipientRewriteTable();
+    public AbstractRecipientRewriteTable virtualUserTable() {
+        return recipientRewriteTable;
     }
 }


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