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