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 d254050d80b1509a3682f9804f31a3b84d2614b2
Author: Rene Cordier <rcord...@linagora.com>
AuthorDate: Fri Apr 10 17:34:52 2020 +0700

    JAMES-3138 Add resetting current quotas service
---
 .../apache/james/mailbox/model/CurrentQuotas.java  |  10 +
 .../apache/james/mailbox/model/QuotaOperation.java |   4 +
 mailbox/cassandra/pom.xml                          |   5 +
 .../quota/CassandraCurrentQuotaManager.java        |  11 +-
 .../CassandraMessageIdManagerQuotaTest.java        |   5 +-
 .../cassandra/CassandraTestSystemFixture.java      |  10 +-
 ...CassandraRecomputeCurrentQuotasServiceTest.java | 160 ++++++++++++++++
 mailbox/jpa/pom.xml                                |   5 +
 .../task/JPARecomputeCurrentQuotasServiceTest.java | 128 +++++++++++++
 mailbox/memory/pom.xml                             |   5 +
 .../MemoryRecomputeCurrentQuotasServiceTest.java   |  83 +++++++++
 .../manager/InMemoryIntegrationResources.java      |  13 +-
 mailbox/store/pom.xml                              |   4 +
 .../mail/task/RecomputeCurrentQuotasService.java   | 170 +++++++++++++++++
 .../store/AbstractMessageIdManagerQuotaTest.java   |   7 +-
 .../RecomputeCurrentQuotasServiceContract.java     | 205 +++++++++++++++++++++
 16 files changed, 807 insertions(+), 18 deletions(-)

diff --git 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/CurrentQuotas.java 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/CurrentQuotas.java
index 38acbaf..f6f0821 100644
--- 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/CurrentQuotas.java
+++ 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/CurrentQuotas.java
@@ -24,6 +24,8 @@ import java.util.Objects;
 import org.apache.james.core.quota.QuotaCountUsage;
 import org.apache.james.core.quota.QuotaSizeUsage;
 
+import com.google.common.base.MoreObjects;
+
 public class CurrentQuotas {
     private final QuotaCountUsage count;
     private final QuotaSizeUsage size;
@@ -76,4 +78,12 @@ public class CurrentQuotas {
     public final int hashCode() {
         return Objects.hash(count, size);
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("count", count)
+            .add("size", size)
+            .toString();
+    }
 }
diff --git 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/QuotaOperation.java 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/QuotaOperation.java
index 7fbd3ba..0bd7007 100644
--- 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/QuotaOperation.java
+++ 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/QuotaOperation.java
@@ -31,6 +31,10 @@ public class QuotaOperation {
     private final QuotaCountUsage count;
     private final QuotaSizeUsage size;
 
+    public static QuotaOperation from(QuotaRoot quotaRoot, CurrentQuotas 
currentQuotas) {
+        return new QuotaOperation(quotaRoot, currentQuotas.count(), 
currentQuotas.size());
+    }
+
     public QuotaOperation(QuotaRoot quotaRoot, QuotaCountUsage count, 
QuotaSizeUsage size) {
         Preconditions.checkArgument(count.asLong() >= 0, "Count should be 
positive");
         Preconditions.checkArgument(size.asLong() >= 0, "Size should be 
positive");
diff --git a/mailbox/cassandra/pom.xml b/mailbox/cassandra/pom.xml
index ddc9764..9cbfceb 100644
--- a/mailbox/cassandra/pom.xml
+++ b/mailbox/cassandra/pom.xml
@@ -84,6 +84,11 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-data-cassandra</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-task-api</artifactId>
         </dependency>
         <dependency>
diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/quota/CassandraCurrentQuotaManager.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/quota/CassandraCurrentQuotaManager.java
index fcda0ca..624d003 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/quota/CassandraCurrentQuotaManager.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/quota/CassandraCurrentQuotaManager.java
@@ -26,6 +26,7 @@ import static 
com.datastax.driver.core.querybuilder.QueryBuilder.incr;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
 import static 
org.apache.james.mailbox.cassandra.table.CassandraCurrentQuota.MESSAGE_COUNT;
+import static 
org.apache.james.mailbox.cassandra.table.CassandraCurrentQuota.QUOTA_ROOT;
 import static 
org.apache.james.mailbox.cassandra.table.CassandraCurrentQuota.STORAGE;
 import static 
org.apache.james.mailbox.cassandra.table.CassandraCurrentQuota.TABLE_NAME;
 
@@ -60,20 +61,20 @@ public class CassandraCurrentQuotaManager implements 
StoreCurrentQuotaManager {
         this.increaseStatement = session.prepare(update(TABLE_NAME)
             .with(incr(MESSAGE_COUNT, bindMarker()))
             .and(incr(STORAGE, bindMarker()))
-            .where(eq(CassandraCurrentQuota.QUOTA_ROOT, bindMarker())));
+            .where(eq(QUOTA_ROOT, bindMarker())));
         this.decreaseStatement = session.prepare(update(TABLE_NAME)
             .with(decr(MESSAGE_COUNT, bindMarker()))
             .and(decr(STORAGE, bindMarker()))
-            .where(eq(CassandraCurrentQuota.QUOTA_ROOT, bindMarker())));
+            .where(eq(QUOTA_ROOT, bindMarker())));
         this.getCurrentMessageCountStatement = 
session.prepare(select(MESSAGE_COUNT)
             .from(TABLE_NAME)
-            .where(eq(CassandraCurrentQuota.QUOTA_ROOT, bindMarker())));
+            .where(eq(QUOTA_ROOT, bindMarker())));
         this.getCurrentStorageStatement = session.prepare(select(STORAGE)
             .from(TABLE_NAME)
-            .where(eq(CassandraCurrentQuota.QUOTA_ROOT, bindMarker())));
+            .where(eq(QUOTA_ROOT, bindMarker())));
         this.getCurrentQuotasStatement = session.prepare(select(MESSAGE_COUNT, 
STORAGE)
             .from(TABLE_NAME)
-            .where(eq(CassandraCurrentQuota.QUOTA_ROOT, bindMarker())));
+            .where(eq(QUOTA_ROOT, bindMarker())));
     }
 
     @Override
diff --git 
a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerQuotaTest.java
 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerQuotaTest.java
index 2293ade..675232f 100644
--- 
a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerQuotaTest.java
+++ 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerQuotaTest.java
@@ -26,6 +26,7 @@ import org.apache.james.mailbox.quota.MaxQuotaManager;
 import org.apache.james.mailbox.quota.QuotaManager;
 import org.apache.james.mailbox.store.AbstractMessageIdManagerQuotaTest;
 import org.apache.james.mailbox.store.MessageIdManagerTestSystem;
+import org.apache.james.mailbox.store.quota.StoreCurrentQuotaManager;
 import org.apache.james.mailbox.store.quota.StoreQuotaManager;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
@@ -45,12 +46,12 @@ class CassandraMessageIdManagerQuotaTest extends 
AbstractMessageIdManagerQuotaTe
     }
 
     @Override
-    protected QuotaManager createQuotaManager(MaxQuotaManager maxQuotaManager, 
CurrentQuotaManager currentQuotaManager) {
+    protected QuotaManager createQuotaManager(MaxQuotaManager maxQuotaManager, 
StoreCurrentQuotaManager currentQuotaManager) {
         return new StoreQuotaManager(currentQuotaManager, maxQuotaManager);
     }
 
     @Override
-    protected CurrentQuotaManager createCurrentQuotaManager() {
+    protected StoreCurrentQuotaManager createCurrentQuotaManager() {
         return 
CassandraTestSystemFixture.createCurrentQuotaManager(cassandraCluster.getCassandraCluster());
     }
 }
diff --git 
a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
index b55ea11..346d6cb 100644
--- 
a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
+++ 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
@@ -36,7 +36,6 @@ import org.apache.james.mailbox.events.EventBusTestFixture;
 import org.apache.james.mailbox.events.InVMEventBus;
 import org.apache.james.mailbox.events.MemoryEventDeadLetters;
 import org.apache.james.mailbox.events.delivery.InVmEventDelivery;
-import org.apache.james.mailbox.quota.CurrentQuotaManager;
 import org.apache.james.mailbox.quota.MaxQuotaManager;
 import org.apache.james.mailbox.quota.QuotaManager;
 import org.apache.james.mailbox.store.Authenticator;
@@ -53,19 +52,20 @@ import 
org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
 import org.apache.james.mailbox.store.mail.model.impl.MessageParser;
 import org.apache.james.mailbox.store.quota.DefaultUserQuotaRootResolver;
 import org.apache.james.mailbox.store.quota.QuotaComponents;
+import org.apache.james.mailbox.store.quota.StoreCurrentQuotaManager;
 import org.apache.james.mailbox.store.search.MessageSearchIndex;
 import org.apache.james.mailbox.store.search.SimpleMessageSearchIndex;
 import org.apache.james.metrics.tests.RecordingMetricFactory;
 
-class CassandraTestSystemFixture {
+public class CassandraTestSystemFixture {
 
-    static CassandraMailboxSessionMapperFactory 
createMapperFactory(CassandraCluster cassandra) {
+    public static CassandraMailboxSessionMapperFactory 
createMapperFactory(CassandraCluster cassandra) {
         CassandraMessageId.Factory messageIdFactory = new 
CassandraMessageId.Factory();
 
         return TestCassandraMailboxSessionMapperFactory.forTests(cassandra, 
messageIdFactory);
     }
 
-    static CassandraMailboxManager 
createMailboxManager(CassandraMailboxSessionMapperFactory mapperFactory) {
+    public static CassandraMailboxManager 
createMailboxManager(CassandraMailboxSessionMapperFactory mapperFactory) {
         InVMEventBus eventBus = new InVMEventBus(new InVmEventDelivery(new 
RecordingMetricFactory()), EventBusTestFixture.RETRY_BACKOFF_CONFIGURATION, new 
MemoryEventDeadLetters());
         StoreRightManager storeRightManager = new 
StoreRightManager(mapperFactory, new UnionMailboxACLResolver(), new 
SimpleGroupMembershipResolver(), eventBus);
         StoreMailboxAnnotationManager annotationManager = new 
StoreMailboxAnnotationManager(mapperFactory, storeRightManager);
@@ -104,7 +104,7 @@ class CassandraTestSystemFixture {
             new CassandraGlobalMaxQuotaDao(cassandra.getConf()));
     }
 
-    static CurrentQuotaManager createCurrentQuotaManager(CassandraCluster 
cassandra) {
+    public static StoreCurrentQuotaManager 
createCurrentQuotaManager(CassandraCluster cassandra) {
         return new CassandraCurrentQuotaManager(cassandra.getConf());
     }
 
diff --git 
a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/CassandraRecomputeCurrentQuotasServiceTest.java
 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/CassandraRecomputeCurrentQuotasServiceTest.java
new file mode 100644
index 0000000..fc53f32
--- /dev/null
+++ 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/CassandraRecomputeCurrentQuotasServiceTest.java
@@ -0,0 +1,160 @@
+/****************************************************************
+ * 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.mailbox.cassandra.mail.task;
+
+import static org.apache.james.backends.cassandra.Scenario.Builder.fail;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.domainlist.cassandra.CassandraDomainListModule;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.SessionProvider;
+import org.apache.james.mailbox.cassandra.CassandraMailboxSessionMapperFactory;
+import org.apache.james.mailbox.cassandra.CassandraTestSystemFixture;
+import org.apache.james.mailbox.cassandra.mail.MailboxAggregateModule;
+import org.apache.james.mailbox.quota.UserQuotaRootResolver;
+import org.apache.james.mailbox.store.StoreMailboxManager;
+import org.apache.james.mailbox.store.mail.task.RecomputeCurrentQuotasService;
+import 
org.apache.james.mailbox.store.mail.task.RecomputeCurrentQuotasServiceContract;
+import org.apache.james.mailbox.store.quota.CurrentQuotaCalculator;
+import org.apache.james.mailbox.store.quota.DefaultUserQuotaRootResolver;
+import org.apache.james.mailbox.store.quota.StoreCurrentQuotaManager;
+import org.apache.james.sieve.cassandra.CassandraSieveRepositoryModule;
+import org.apache.james.task.Task;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.cassandra.CassandraUsersDAO;
+import org.apache.james.user.cassandra.CassandraUsersRepositoryModule;
+import org.apache.james.user.lib.UsersRepositoryImpl;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import com.google.common.collect.ImmutableList;
+
+public class CassandraRecomputeCurrentQuotasServiceTest implements 
RecomputeCurrentQuotasServiceContract {
+    static final DomainList NO_DOMAIN_LIST = null;
+
+    @RegisterExtension
+    static CassandraClusterExtension cassandraCluster = new 
CassandraClusterExtension(CassandraModule.aggregateModules(
+        MailboxAggregateModule.MODULE_WITH_QUOTA,
+        CassandraDomainListModule.MODULE,
+        CassandraSieveRepositoryModule.MODULE,
+        CassandraUsersRepositoryModule.MODULE));
+
+    UsersRepositoryImpl usersRepository;
+    StoreMailboxManager mailboxManager;
+    SessionProvider sessionProvider;
+    StoreCurrentQuotaManager currentQuotaManager;
+    UserQuotaRootResolver userQuotaRootResolver;
+    RecomputeCurrentQuotasService testee;
+
+    @BeforeEach
+    void setUp() {
+        CassandraCluster cassandra = cassandraCluster.getCassandraCluster();
+        CassandraMailboxSessionMapperFactory mapperFactory = 
CassandraTestSystemFixture.createMapperFactory(cassandra);
+
+        CassandraUsersDAO usersDAO = new 
CassandraUsersDAO(cassandra.getConf());
+        usersRepository = new UsersRepositoryImpl(NO_DOMAIN_LIST, usersDAO);
+        usersRepository.setEnableVirtualHosting(false);
+
+        mailboxManager = 
CassandraTestSystemFixture.createMailboxManager(mapperFactory);
+        sessionProvider  = mailboxManager.getSessionProvider();
+        currentQuotaManager = 
CassandraTestSystemFixture.createCurrentQuotaManager(cassandra);
+        userQuotaRootResolver = new 
DefaultUserQuotaRootResolver(sessionProvider, mapperFactory);
+        CurrentQuotaCalculator currentQuotaCalculator = new 
CurrentQuotaCalculator(mapperFactory, userQuotaRootResolver);
+
+        testee = new RecomputeCurrentQuotasService(usersRepository, 
currentQuotaManager, currentQuotaCalculator, userQuotaRootResolver, 
sessionProvider);
+    }
+
+    @Override
+    public UsersRepository usersRepository() {
+        return usersRepository;
+    }
+
+    @Override
+    public SessionProvider sessionProvider() {
+        return sessionProvider;
+    }
+
+    @Override
+    public MailboxManager mailboxManager() {
+        return mailboxManager;
+    }
+
+    @Override
+    public StoreCurrentQuotaManager currentQuotaManager() {
+        return currentQuotaManager;
+    }
+
+    @Override
+    public UserQuotaRootResolver userQuotaRootResolver() {
+        return userQuotaRootResolver;
+    }
+
+    @Override
+    public RecomputeCurrentQuotasService testee() {
+        return testee;
+    }
+
+    @Test
+    void recomputeCurrentQuotasShouldReturnPartialWhenFailureAtReset() throws 
Exception {
+        usersRepository().addUser(USER_1, PASSWORD);
+
+        MailboxSession session = sessionProvider().createSystemSession(USER_1);
+        mailboxManager().createMailbox(MAILBOX_PATH, session);
+
+        MessageManager messageManager = 
mailboxManager().getMailbox(MAILBOX_PATH, session);
+        appendAMessageForUser(messageManager, session);
+
+        
cassandraCluster.getCassandraCluster().getConf().registerScenario(fail()
+            .times(1)
+            .whenQueryStartsWith("UPDATE currentQuota SET"));
+
+        assertThat(testee().recomputeCurrentQuotas(new 
RecomputeCurrentQuotasService.Context()).block())
+            .isEqualTo(Task.Result.PARTIAL);
+    }
+
+    @Test
+    void recomputeCurrentQuotasShouldUpdateContextWhenFailureAtReset() throws 
Exception {
+        usersRepository().addUser(USER_1, PASSWORD);
+
+        MailboxSession session = sessionProvider().createSystemSession(USER_1);
+        mailboxManager().createMailbox(MAILBOX_PATH, session);
+
+        MessageManager messageManager = 
mailboxManager().getMailbox(MAILBOX_PATH, session);
+        appendAMessageForUser(messageManager, session);
+
+        
cassandraCluster.getCassandraCluster().getConf().registerScenario(fail()
+            .times(1)
+            .whenQueryStartsWith("UPDATE currentQuota SET"));
+
+        RecomputeCurrentQuotasService.Context context = new 
RecomputeCurrentQuotasService.Context();
+        testee().recomputeCurrentQuotas(context).block();
+
+        assertThat(context.snapshot())
+            .isEqualTo(new RecomputeCurrentQuotasService.Context(0L,
+                
ImmutableList.of(userQuotaRootResolver.forUser(USER_1))).snapshot());
+    }
+}
diff --git a/mailbox/jpa/pom.xml b/mailbox/jpa/pom.xml
index d148681..d6538ae 100644
--- a/mailbox/jpa/pom.xml
+++ b/mailbox/jpa/pom.xml
@@ -69,6 +69,11 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-data-jpa</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-util</artifactId>
         </dependency>
         <dependency>
diff --git 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/task/JPARecomputeCurrentQuotasServiceTest.java
 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/task/JPARecomputeCurrentQuotasServiceTest.java
new file mode 100644
index 0000000..95ba065
--- /dev/null
+++ 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/task/JPARecomputeCurrentQuotasServiceTest.java
@@ -0,0 +1,128 @@
+/****************************************************************
+ * 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.mailbox.jpa.mail.task;
+
+import javax.persistence.EntityManagerFactory;
+
+import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
+import org.apache.james.backends.jpa.JpaTestCluster;
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.SessionProvider;
+import org.apache.james.mailbox.jpa.JPAMailboxFixture;
+import org.apache.james.mailbox.jpa.JPAMailboxSessionMapperFactory;
+import org.apache.james.mailbox.jpa.JpaMailboxManagerProvider;
+import org.apache.james.mailbox.jpa.mail.JPAModSeqProvider;
+import org.apache.james.mailbox.jpa.mail.JPAUidProvider;
+import org.apache.james.mailbox.jpa.quota.JpaCurrentQuotaManager;
+import org.apache.james.mailbox.quota.UserQuotaRootResolver;
+import org.apache.james.mailbox.store.StoreMailboxManager;
+import org.apache.james.mailbox.store.mail.task.RecomputeCurrentQuotasService;
+import 
org.apache.james.mailbox.store.mail.task.RecomputeCurrentQuotasServiceContract;
+import org.apache.james.mailbox.store.quota.CurrentQuotaCalculator;
+import org.apache.james.mailbox.store.quota.DefaultUserQuotaRootResolver;
+import org.apache.james.mailbox.store.quota.StoreCurrentQuotaManager;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.jpa.JPAUsersRepository;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+
+import com.google.common.collect.ImmutableList;
+
+@Disabled("JAMES-3138 Broken...")
+class JPARecomputeCurrentQuotasServiceTest implements 
RecomputeCurrentQuotasServiceContract {
+
+    static final DomainList NO_DOMAIN_LIST = null;
+
+    static final JpaTestCluster JPA_TEST_CLUSTER = 
JpaTestCluster.create(ImmutableList.<Class<?>>builder()
+        .addAll(JPAMailboxFixture.MAILBOX_PERSISTANCE_CLASSES)
+        .addAll(JPAMailboxFixture.QUOTA_PERSISTANCE_CLASSES)
+        .build());
+
+    JPAUsersRepository usersRepository;
+    StoreMailboxManager mailboxManager;
+    SessionProvider sessionProvider;
+    StoreCurrentQuotaManager currentQuotaManager;
+    UserQuotaRootResolver userQuotaRootResolver;
+    RecomputeCurrentQuotasService testee;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        EntityManagerFactory entityManagerFactory = 
JPA_TEST_CLUSTER.getEntityManagerFactory();
+        JPAMailboxSessionMapperFactory mapperFactory = new 
JPAMailboxSessionMapperFactory(entityManagerFactory,
+            new JPAUidProvider(entityManagerFactory),
+            new JPAModSeqProvider(entityManagerFactory));
+
+        usersRepository = new JPAUsersRepository(NO_DOMAIN_LIST);
+        
usersRepository.setEntityManagerFactory(JPA_TEST_CLUSTER.getEntityManagerFactory());
+        BaseHierarchicalConfiguration configuration = new 
BaseHierarchicalConfiguration();
+        configuration.addProperty("enableVirtualHosting", "false");
+        usersRepository.configure(configuration);
+
+        mailboxManager = 
JpaMailboxManagerProvider.provideMailboxManager(JPA_TEST_CLUSTER);
+        sessionProvider = mailboxManager.getSessionProvider();
+        currentQuotaManager = new JpaCurrentQuotaManager(entityManagerFactory);
+
+        userQuotaRootResolver = new 
DefaultUserQuotaRootResolver(sessionProvider, mapperFactory);
+
+        CurrentQuotaCalculator currentQuotaCalculator = new 
CurrentQuotaCalculator(mapperFactory, userQuotaRootResolver);
+
+        testee = new RecomputeCurrentQuotasService(usersRepository, 
currentQuotaManager, currentQuotaCalculator, userQuotaRootResolver, 
sessionProvider);
+    }
+
+    @AfterEach
+    void tearDownJpa() {
+        JPA_TEST_CLUSTER.clear(ImmutableList.<String>builder()
+            .addAll(JPAMailboxFixture.MAILBOX_TABLE_NAMES)
+            .addAll(JPAMailboxFixture.QUOTA_TABLES_NAMES)
+            .build());
+    }
+
+    @Override
+    public UsersRepository usersRepository() {
+        return usersRepository;
+    }
+
+    @Override
+    public SessionProvider sessionProvider() {
+        return sessionProvider;
+    }
+
+    @Override
+    public MailboxManager mailboxManager() {
+        return mailboxManager;
+    }
+
+    @Override
+    public StoreCurrentQuotaManager currentQuotaManager() {
+        return currentQuotaManager;
+    }
+
+    @Override
+    public UserQuotaRootResolver userQuotaRootResolver() {
+        return userQuotaRootResolver;
+    }
+
+    @Override
+    public RecomputeCurrentQuotasService testee() {
+        return testee;
+    }
+}
diff --git a/mailbox/memory/pom.xml b/mailbox/memory/pom.xml
index a460862..5676830 100644
--- a/mailbox/memory/pom.xml
+++ b/mailbox/memory/pom.xml
@@ -54,6 +54,11 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-data-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-util</artifactId>
         </dependency>
         <dependency>
diff --git 
a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/task/MemoryRecomputeCurrentQuotasServiceTest.java
 
b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/task/MemoryRecomputeCurrentQuotasServiceTest.java
new file mode 100644
index 0000000..f9a550d
--- /dev/null
+++ 
b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/task/MemoryRecomputeCurrentQuotasServiceTest.java
@@ -0,0 +1,83 @@
+/****************************************************************
+ * 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.mailbox.inmemory.mail.task;
+
+import static org.mockito.Mockito.mock;
+
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.domainlist.memory.MemoryDomainList;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.SessionProvider;
+import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
+import org.apache.james.mailbox.quota.UserQuotaRootResolver;
+import org.apache.james.mailbox.store.mail.task.RecomputeCurrentQuotasService;
+import 
org.apache.james.mailbox.store.mail.task.RecomputeCurrentQuotasServiceContract;
+import org.apache.james.mailbox.store.quota.StoreCurrentQuotaManager;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.memory.MemoryUsersRepository;
+import org.junit.jupiter.api.BeforeEach;
+
+class MemoryRecomputeCurrentQuotasServiceTest implements 
RecomputeCurrentQuotasServiceContract {
+
+    MemoryUsersRepository usersRepository;
+    InMemoryIntegrationResources resources;
+    RecomputeCurrentQuotasService testee;
+
+    @BeforeEach
+    void setUp() {
+        DNSService dnsService = mock(DNSService.class);
+        MemoryDomainList memoryDomainList = new MemoryDomainList(dnsService);
+        memoryDomainList.setAutoDetect(false);
+        usersRepository = 
MemoryUsersRepository.withoutVirtualHosting(memoryDomainList);
+
+        resources = InMemoryIntegrationResources.defaultResources();
+        testee = new RecomputeCurrentQuotasService(usersRepository, 
resources.getCurrentQuotaManager(), resources.getCurrentQuotaCalculator(), 
resources.getDefaultUserQuotaRootResolver(), 
resources.getMailboxManager().getSessionProvider());
+    }
+
+    @Override
+    public RecomputeCurrentQuotasService testee() {
+        return testee;
+    }
+
+    @Override
+    public UsersRepository usersRepository() {
+        return usersRepository;
+    }
+
+    @Override
+    public SessionProvider sessionProvider() {
+        return resources.getMailboxManager().getSessionProvider();
+    }
+
+    @Override
+    public MailboxManager mailboxManager() {
+        return resources.getMailboxManager();
+    }
+
+    @Override
+    public StoreCurrentQuotaManager currentQuotaManager() {
+        return resources.getCurrentQuotaManager();
+    }
+
+    @Override
+    public UserQuotaRootResolver userQuotaRootResolver() {
+        return resources.getDefaultUserQuotaRootResolver();
+    }
+}
diff --git 
a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java
 
b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java
index 61e0c8f..b2027a1 100644
--- 
a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java
+++ 
b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java
@@ -306,7 +306,8 @@ public class InMemoryIntegrationResources implements 
IntegrationResources<StoreM
 
             InMemoryPerUserMaxQuotaManager maxQuotaManager = new 
InMemoryPerUserMaxQuotaManager();
             DefaultUserQuotaRootResolver quotaRootResolver = new 
DefaultUserQuotaRootResolver(sessionProvider, mailboxSessionMapperFactory);
-            InMemoryCurrentQuotaManager currentQuotaManager = new 
InMemoryCurrentQuotaManager(new 
CurrentQuotaCalculator(mailboxSessionMapperFactory, quotaRootResolver), 
sessionProvider);
+            CurrentQuotaCalculator currentQuotaCalculator = new 
CurrentQuotaCalculator(mailboxSessionMapperFactory, quotaRootResolver);
+            InMemoryCurrentQuotaManager currentQuotaManager = new 
InMemoryCurrentQuotaManager(currentQuotaCalculator, sessionProvider);
             QuotaManager quotaManager = this.quotaManager.get().apply(new 
BaseQuotaComponentsStage(maxQuotaManager, currentQuotaManager));
             ListeningCurrentQuotaUpdater listeningCurrentQuotaUpdater = new 
ListeningCurrentQuotaUpdater(currentQuotaManager, quotaRootResolver, eventBus, 
quotaManager);
             QuotaComponents quotaComponents = new 
QuotaComponents(maxQuotaManager, quotaManager, quotaRootResolver);
@@ -342,7 +343,7 @@ public class InMemoryIntegrationResources implements 
IntegrationResources<StoreM
 
             StoreBlobManager blobManager = new 
StoreBlobManager(attachmentManager, messageIdManager, messageIdFactory);
 
-            return new InMemoryIntegrationResources(manager, 
storeRightManager, messageIdFactory, currentQuotaManager, quotaRootResolver, 
maxQuotaManager, quotaManager, messageIdManager, index, eventBus, blobManager);
+            return new InMemoryIntegrationResources(manager, 
storeRightManager, messageIdFactory, currentQuotaCalculator, 
currentQuotaManager, quotaRootResolver, maxQuotaManager, quotaManager, 
messageIdManager, index, eventBus, blobManager);
         }
 
         private PreDeletionHooks 
createHooks(MailboxManagerPreInstanciationStage preInstanciationStage) {
@@ -417,6 +418,7 @@ public class InMemoryIntegrationResources implements 
IntegrationResources<StoreM
     private final InMemoryMailboxManager mailboxManager;
     private final StoreRightManager storeRightManager;
     private final MessageId.Factory messageIdFactory;
+    private final CurrentQuotaCalculator currentQuotaCalculator;
     private final InMemoryCurrentQuotaManager currentQuotaManager;
     private final DefaultUserQuotaRootResolver defaultUserQuotaRootResolver;
     private final InMemoryPerUserMaxQuotaManager maxQuotaManager;
@@ -426,10 +428,11 @@ public class InMemoryIntegrationResources implements 
IntegrationResources<StoreM
     private final EventBus eventBus;
     private final StoreBlobManager blobManager;
 
-    InMemoryIntegrationResources(InMemoryMailboxManager mailboxManager, 
StoreRightManager storeRightManager, MessageId.Factory messageIdFactory, 
InMemoryCurrentQuotaManager currentQuotaManager, DefaultUserQuotaRootResolver 
defaultUserQuotaRootResolver, InMemoryPerUserMaxQuotaManager maxQuotaManager, 
QuotaManager quotaManager, StoreMessageIdManager storeMessageIdManager, 
MessageSearchIndex searchIndex, EventBus eventBus, StoreBlobManager 
blobManager) {
+    InMemoryIntegrationResources(InMemoryMailboxManager mailboxManager, 
StoreRightManager storeRightManager, MessageId.Factory messageIdFactory, 
CurrentQuotaCalculator currentQuotaCalculator, InMemoryCurrentQuotaManager 
currentQuotaManager, DefaultUserQuotaRootResolver defaultUserQuotaRootResolver, 
InMemoryPerUserMaxQuotaManager maxQuotaManager, QuotaManager quotaManager, 
StoreMessageIdManager storeMessageIdManager, MessageSearchIndex searchIndex, 
EventBus eventBus, StoreBlobManager blob [...]
         this.mailboxManager = mailboxManager;
         this.storeRightManager = storeRightManager;
         this.messageIdFactory = messageIdFactory;
+        this.currentQuotaCalculator = currentQuotaCalculator;
         this.currentQuotaManager = currentQuotaManager;
         this.defaultUserQuotaRootResolver = defaultUserQuotaRootResolver;
         this.maxQuotaManager = maxQuotaManager;
@@ -448,6 +451,10 @@ public class InMemoryIntegrationResources implements 
IntegrationResources<StoreM
         return mailboxManager;
     }
 
+    public CurrentQuotaCalculator getCurrentQuotaCalculator() {
+        return currentQuotaCalculator;
+    }
+
     public InMemoryCurrentQuotaManager getCurrentQuotaManager() {
         return currentQuotaManager;
     }
diff --git a/mailbox/store/pom.xml b/mailbox/store/pom.xml
index 2868ef7..2785e64 100644
--- a/mailbox/store/pom.xml
+++ b/mailbox/store/pom.xml
@@ -69,6 +69,10 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-util</artifactId>
         </dependency>
         <dependency>
diff --git 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/task/RecomputeCurrentQuotasService.java
 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/task/RecomputeCurrentQuotasService.java
new file mode 100644
index 0000000..e6ae74e
--- /dev/null
+++ 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/task/RecomputeCurrentQuotasService.java
@@ -0,0 +1,170 @@
+/****************************************************************
+ * 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.mailbox.store.mail.task;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.inject.Inject;
+
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.SessionProvider;
+import org.apache.james.mailbox.model.QuotaOperation;
+import org.apache.james.mailbox.model.QuotaRoot;
+import org.apache.james.mailbox.quota.UserQuotaRootResolver;
+import org.apache.james.mailbox.store.quota.CurrentQuotaCalculator;
+import org.apache.james.mailbox.store.quota.StoreCurrentQuotaManager;
+import org.apache.james.task.Task;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.api.UsersRepositoryException;
+import org.apache.james.util.streams.Iterators;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+
+import reactor.core.publisher.Mono;
+
+public class RecomputeCurrentQuotasService {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(RecomputeCurrentQuotasService.class);
+
+    public static class Context {
+        static class Snapshot {
+            private final long processedQuotaRootCount;
+            private final ImmutableList<QuotaRoot> failedQuotaRoots;
+
+            private Snapshot(long processedQuotaRootCount, 
ImmutableList<QuotaRoot> failedQuotaRoots) {
+                this.processedQuotaRootCount = processedQuotaRootCount;
+                this.failedQuotaRoots = failedQuotaRoots;
+            }
+
+            long getProcessedQuotaRootCount() {
+                return processedQuotaRootCount;
+            }
+
+            ImmutableList<QuotaRoot> getFailedQuotaRoots() {
+                return failedQuotaRoots;
+            }
+
+            @Override
+            public final boolean equals(Object o) {
+                if (o instanceof Snapshot) {
+                    Snapshot that = (Snapshot) o;
+
+                    return Objects.equals(this.processedQuotaRootCount, 
that.processedQuotaRootCount)
+                        && Objects.equals(this.failedQuotaRoots, 
that.failedQuotaRoots);
+                }
+                return false;
+            }
+
+            @Override
+            public final int hashCode() {
+                return Objects.hash(processedQuotaRootCount, failedQuotaRoots);
+            }
+
+            @Override
+            public String toString() {
+                return MoreObjects.toStringHelper(this)
+                    .add("processedQuotaRootCount", processedQuotaRootCount)
+                    .add("failedQuotaRoots", failedQuotaRoots)
+                    .toString();
+            }
+        }
+
+        private final AtomicLong processedQuotaRootCount;
+        private final ConcurrentLinkedDeque<QuotaRoot> failedQuotaRoots;
+
+        public Context() {
+            this.processedQuotaRootCount = new AtomicLong();
+            this.failedQuotaRoots = new ConcurrentLinkedDeque<>();
+        }
+
+        public Context(long processedQuotaRootCount, Collection<QuotaRoot> 
failedQuotaRoots) {
+            this.processedQuotaRootCount = new 
AtomicLong(processedQuotaRootCount);
+            this.failedQuotaRoots = new 
ConcurrentLinkedDeque<>(failedQuotaRoots);
+        }
+
+        void incrementProcessed() {
+            processedQuotaRootCount.incrementAndGet();
+        }
+
+        void addToFailedMailboxes(QuotaRoot quotaRoot) {
+            failedQuotaRoots.add(quotaRoot);
+        }
+
+        public Snapshot snapshot() {
+            return new Snapshot(processedQuotaRootCount.get(),
+                ImmutableList.copyOf(failedQuotaRoots));
+        }
+    }
+
+    private final UsersRepository usersRepository;
+    private final StoreCurrentQuotaManager storeCurrentQuotaManager;
+    private final CurrentQuotaCalculator currentQuotaCalculator;
+    private final UserQuotaRootResolver userQuotaRootResolver;
+    private final SessionProvider sessionProvider;
+
+    @Inject
+    public RecomputeCurrentQuotasService(UsersRepository usersRepository,
+                                         StoreCurrentQuotaManager 
storeCurrentQuotaManager,
+                                         CurrentQuotaCalculator 
currentQuotaCalculator,
+                                         UserQuotaRootResolver 
userQuotaRootResolver,
+                                         SessionProvider sessionProvider) {
+        this.usersRepository = usersRepository;
+        this.storeCurrentQuotaManager = storeCurrentQuotaManager;
+        this.currentQuotaCalculator = currentQuotaCalculator;
+        this.userQuotaRootResolver = userQuotaRootResolver;
+        this.sessionProvider = sessionProvider;
+    }
+
+    public Mono<Task.Result> recomputeCurrentQuotas(Context context) {
+        try {
+            return Iterators.toFlux(usersRepository.list())
+                .flatMap(username -> recomputeUserCurrentQuotas(context, 
username))
+                .reduce(Task.Result.COMPLETED, Task::combine);
+        } catch (UsersRepositoryException e) {
+            LOGGER.error("Error while accessing users from repository", e);
+            return Mono.just(Task.Result.PARTIAL);
+        }
+    }
+
+    private Mono<Task.Result> recomputeUserCurrentQuotas(Context context, 
Username username) {
+        MailboxSession session = sessionProvider.createSystemSession(username);
+        QuotaRoot quotaRoot = userQuotaRootResolver.forUser(username);
+
+        return Mono.fromCallable(() -> 
currentQuotaCalculator.recalculateCurrentQuotas(quotaRoot, session))
+            .map(recalculatedQuotas -> QuotaOperation.from(quotaRoot, 
recalculatedQuotas))
+            .flatMap(storeCurrentQuotaManager::resetCurrentQuotas)
+            .then(Mono.just(Task.Result.COMPLETED))
+            .doOnNext(any -> {
+                LOGGER.info("Current quotas recomputed for {}", quotaRoot);
+                context.incrementProcessed();
+            })
+            .onErrorResume(e -> {
+                LOGGER.error("Error while recomputing current quotas for {}", 
quotaRoot, e);
+                context.addToFailedMailboxes(quotaRoot);
+                return Mono.just(Task.Result.PARTIAL);
+            });
+    }
+}
diff --git 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerQuotaTest.java
 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerQuotaTest.java
index 8624426..34c4efb 100644
--- 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerQuotaTest.java
+++ 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerQuotaTest.java
@@ -37,6 +37,7 @@ import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.quota.CurrentQuotaManager;
 import org.apache.james.mailbox.quota.MaxQuotaManager;
 import org.apache.james.mailbox.quota.QuotaManager;
+import org.apache.james.mailbox.store.quota.StoreCurrentQuotaManager;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -59,14 +60,14 @@ public abstract class AbstractMessageIdManagerQuotaTest {
 
     protected abstract MaxQuotaManager createMaxQuotaManager();
     
-    protected abstract CurrentQuotaManager createCurrentQuotaManager();
+    protected abstract StoreCurrentQuotaManager createCurrentQuotaManager();
     
-    protected abstract QuotaManager createQuotaManager(MaxQuotaManager 
maxQuotaManager, CurrentQuotaManager currentQuotaManager);
+    protected abstract QuotaManager createQuotaManager(MaxQuotaManager 
maxQuotaManager, StoreCurrentQuotaManager currentQuotaManager);
 
     @BeforeEach
     void setUp() throws Exception {
         maxQuotaManager = createMaxQuotaManager();
-        CurrentQuotaManager currentQuotaManager = createCurrentQuotaManager();
+        StoreCurrentQuotaManager currentQuotaManager = 
createCurrentQuotaManager();
         QuotaManager quotaManager = createQuotaManager(maxQuotaManager, 
currentQuotaManager);
 
         session = MailboxSessionUtil.create(ALICE);
diff --git 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/task/RecomputeCurrentQuotasServiceContract.java
 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/task/RecomputeCurrentQuotasServiceContract.java
new file mode 100644
index 0000000..d4bb3b0
--- /dev/null
+++ 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/task/RecomputeCurrentQuotasServiceContract.java
@@ -0,0 +1,205 @@
+/****************************************************************
+ * 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.mailbox.store.mail.task;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.james.core.Username;
+import org.apache.james.core.quota.QuotaCountUsage;
+import org.apache.james.core.quota.QuotaSizeUsage;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.SessionProvider;
+import org.apache.james.mailbox.model.CurrentQuotas;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.QuotaOperation;
+import org.apache.james.mailbox.model.QuotaRoot;
+import org.apache.james.mailbox.quota.UserQuotaRootResolver;
+import 
org.apache.james.mailbox.store.mail.task.RecomputeCurrentQuotasService.Context;
+import org.apache.james.mailbox.store.quota.StoreCurrentQuotaManager;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.task.Task;
+import org.apache.james.user.api.UsersRepository;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import reactor.core.publisher.Mono;
+
+public interface RecomputeCurrentQuotasServiceContract {
+    Username USER_1 = Username.of("user1");
+    String PASSWORD = "password";
+    MailboxPath MAILBOX_PATH = MailboxPath.forUser(USER_1, "mailbox");
+    CurrentQuotas EXPECTED_QUOTAS = new 
CurrentQuotas(QuotaCountUsage.count(1L), QuotaSizeUsage.size(103L));
+
+    UsersRepository usersRepository();
+    SessionProvider sessionProvider();
+    MailboxManager mailboxManager();
+    StoreCurrentQuotaManager currentQuotaManager();
+    UserQuotaRootResolver userQuotaRootResolver();
+    RecomputeCurrentQuotasService testee();
+
+    @Test
+    default void recomputeCurrentQuotasShouldReturnCompleteWhenNoData() {
+        assertThat(testee().recomputeCurrentQuotas(new Context()).block())
+            .isEqualTo(Task.Result.COMPLETED);
+    }
+
+    @Test
+    default void 
recomputeCurrentQuotasShouldReturnCompleteWhenUserWithNoMessage() throws 
Exception {
+        usersRepository().addUser(USER_1, PASSWORD);
+
+        assertThat(testee().recomputeCurrentQuotas(new Context()).block())
+            .isEqualTo(Task.Result.COMPLETED);
+    }
+
+    @Test
+    default void 
recomputeCurrentQuotasShouldComputeEmptyQuotasWhenUserWithNoMessage() throws 
Exception {
+        usersRepository().addUser(USER_1, PASSWORD);
+
+        testee().recomputeCurrentQuotas(new Context()).block();
+
+        
assertThat(Mono.from(currentQuotaManager().getCurrentQuotas(userQuotaRootResolver().forUser(USER_1))).block())
+            .isEqualTo(CurrentQuotas.emptyQuotas());
+    }
+
+    @Test
+    default void 
recomputeCurrentQuotasShouldReturnCompleteWhenUserWithMessage() throws 
Exception {
+        usersRepository().addUser(USER_1, PASSWORD);
+
+        MailboxSession session = sessionProvider().createSystemSession(USER_1);
+        mailboxManager().createMailbox(MAILBOX_PATH, session);
+
+        MessageManager messageManager = 
mailboxManager().getMailbox(MAILBOX_PATH, session);
+        appendAMessageForUser(messageManager, session);
+
+        assertThat(testee().recomputeCurrentQuotas(new Context()).block())
+            .isEqualTo(Task.Result.COMPLETED);
+    }
+
+    @Test
+    default void 
recomputeCurrentQuotasShouldRecomputeCurrentQuotasCorrectlyWhenUserWithMessage()
 throws Exception {
+        usersRepository().addUser(USER_1, PASSWORD);
+
+        MailboxSession session = sessionProvider().createSystemSession(USER_1);
+        mailboxManager().createMailbox(MAILBOX_PATH, session);
+
+        MessageManager messageManager = 
mailboxManager().getMailbox(MAILBOX_PATH, session);
+        appendAMessageForUser(messageManager, session);
+
+        testee().recomputeCurrentQuotas(new Context()).block();
+
+        
assertThat(Mono.from(currentQuotaManager().getCurrentQuotas(userQuotaRootResolver().forUser(USER_1))).block())
+            .isEqualTo(EXPECTED_QUOTAS);
+    }
+
+    @Test
+    default void 
recomputeCurrentQuotasShouldResetCurrentQuotasWhenIncorrectQuotas() throws 
Exception {
+        usersRepository().addUser(USER_1, PASSWORD);
+
+        MailboxSession session = sessionProvider().createSystemSession(USER_1);
+        mailboxManager().createMailbox(MAILBOX_PATH, session);
+
+        MessageManager messageManager = 
mailboxManager().getMailbox(MAILBOX_PATH, session);
+        appendAMessageForUser(messageManager, session);
+
+        QuotaRoot quotaRoot = userQuotaRootResolver().forUser(USER_1);
+
+        QuotaOperation operation = new QuotaOperation(quotaRoot, 
QuotaCountUsage.count(3L), QuotaSizeUsage.size(390L));
+        currentQuotaManager().increase(operation).block();
+
+        testee().recomputeCurrentQuotas(new Context()).block();
+
+        
assertThat(Mono.from(currentQuotaManager().getCurrentQuotas(userQuotaRootResolver().forUser(USER_1))).block())
+            .isEqualTo(EXPECTED_QUOTAS);
+    }
+
+    @Test
+    default void recomputeCurrentQuotasShouldNotUpdateContextWhenNoData() {
+        Context context = new Context();
+        testee().recomputeCurrentQuotas(context).block();
+
+        
assertThat(context.snapshot()).isEqualToComparingFieldByFieldRecursively(new 
Context().snapshot());
+    }
+
+    @Test
+    default void 
recomputeCurrentQuotasShouldUpdateContextWhenUserWithNoMessage() throws 
Exception {
+        usersRepository().addUser(USER_1, PASSWORD);
+
+        Context context = new Context();
+        testee().recomputeCurrentQuotas(context).block();
+
+        assertThat(context.snapshot())
+            .isEqualTo(new Context(1L, ImmutableList.of()).snapshot());
+    }
+
+    @Test
+    default void 
recomputeCurrentQuotasShouldUpdateContextWhenUserWithMessage() throws Exception 
{
+        usersRepository().addUser(USER_1, PASSWORD);
+
+        MailboxSession session = sessionProvider().createSystemSession(USER_1);
+        mailboxManager().createMailbox(MAILBOX_PATH, session);
+
+        MessageManager messageManager = 
mailboxManager().getMailbox(MAILBOX_PATH, session);
+        appendAMessageForUser(messageManager, session);
+
+        Context context = new Context();
+        testee().recomputeCurrentQuotas(context).block();
+
+        assertThat(context.snapshot())
+            .isEqualTo(new Context(1L, ImmutableList.of()).snapshot());
+    }
+
+    @Test
+    default void 
recomputeCurrentQuotasShouldUpdateContextWhenIncorrectQuotas() throws Exception 
{
+        usersRepository().addUser(USER_1, PASSWORD);
+        usersRepository().addUser(Username.of("user2"), PASSWORD);
+
+        MailboxSession session = sessionProvider().createSystemSession(USER_1);
+        mailboxManager().createMailbox(MAILBOX_PATH, session);
+
+        MessageManager messageManager = 
mailboxManager().getMailbox(MAILBOX_PATH, session);
+        appendAMessageForUser(messageManager, session);
+
+        QuotaRoot quotaRoot = userQuotaRootResolver().forUser(USER_1);
+
+        QuotaOperation operation = new QuotaOperation(quotaRoot, 
QuotaCountUsage.count(3L), QuotaSizeUsage.size(390L));
+        currentQuotaManager().increase(operation).block();
+
+        Context context = new Context();
+        testee().recomputeCurrentQuotas(context).block();
+
+        assertThat(context.snapshot())
+            .isEqualTo(new Context(2L, ImmutableList.of()).snapshot());
+    }
+
+    default void appendAMessageForUser(MessageManager messageManager, 
MailboxSession session) throws Exception {
+        String recipient = "t...@localhost.com";
+        String body = "This is a message";
+        messageManager.appendMessage(MessageManager.AppendCommand.from(
+            Message.Builder.of()
+                .setTo(recipient)
+                .setBody(body, StandardCharsets.UTF_8)),
+            session);
+    }
+}


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

Reply via email to