You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2018/10/02 09:28:39 UTC

[6/9] james-project git commit: JAMES-2544 Implemented an event sourcing system for validating configuration changes

JAMES-2544 Implemented an event sourcing system for validating configuration changes

Temporarily use in memory event store. DTO will be added later on.


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/032c08f0
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/032c08f0
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/032c08f0

Branch: refs/heads/master
Commit: 032c08f08e940a455b209f865a5ee544b507b892
Parents: c147286
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Sep 27 11:16:24 2018 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Oct 2 10:18:32 2018 +0700

----------------------------------------------------------------------
 server/queue/queue-rabbitmq/pom.xml             |   9 +
 .../configuration/ConfigurationAggregate.java   |  96 +++++++
 .../configuration/ConfigurationChanged.java     |  59 +++++
 .../EventsourcingConfigurationManagement.java   |  65 +++++
 .../RegisterConfigurationCommand.java           |  64 +++++
 .../RegisterConfigurationCommandHandler.java    |  47 ++++
 ...ventsourcingConfigurationManagementTest.java | 247 +++++++++++++++++++
 .../RegisterConfigurationCommandTest.java       |  55 +++++
 8 files changed, 642 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/032c08f0/server/queue/queue-rabbitmq/pom.xml
----------------------------------------------------------------------
diff --git a/server/queue/queue-rabbitmq/pom.xml b/server/queue/queue-rabbitmq/pom.xml
index 47b958b..b0fdc90 100644
--- a/server/queue/queue-rabbitmq/pom.xml
+++ b/server/queue/queue-rabbitmq/pom.xml
@@ -68,6 +68,15 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>event-sourcing-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>event-sourcing-event-store-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-core</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/032c08f0/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/ConfigurationAggregate.java
----------------------------------------------------------------------
diff --git a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/ConfigurationAggregate.java b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/ConfigurationAggregate.java
new file mode 100644
index 0000000..ffa3106
--- /dev/null
+++ b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/ConfigurationAggregate.java
@@ -0,0 +1,96 @@
+/****************************************************************
+ * 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.queue.rabbitmq.view.cassandra.configuration;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.eventstore.History;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+class ConfigurationAggregate {
+
+    private static class State {
+
+        private static State initial() {
+            return new State(Optional.empty());
+        }
+
+        private Optional<CassandraMailQueueViewConfiguration> maybeConfiguration;
+
+        State(Optional<CassandraMailQueueViewConfiguration> maybeConfiguration) {
+            Preconditions.checkNotNull(maybeConfiguration);
+            this.maybeConfiguration = maybeConfiguration;
+        }
+
+        State set(CassandraMailQueueViewConfiguration configuration) {
+            return new State(Optional.of(configuration));
+        }
+
+    }
+
+    static ConfigurationAggregate load(AggregateId aggregateId, History history) {
+        return new ConfigurationAggregate(aggregateId, history);
+    }
+
+    private static final List<? extends Event> EMPTY_EVENTS = ImmutableList.of();
+
+    private final AggregateId aggregateId;
+    private final History history;
+    private State state;
+
+    ConfigurationAggregate(AggregateId aggregateId, History history) {
+        this.aggregateId = aggregateId;
+        this.history = history;
+        this.state = State.initial();
+
+        history.getEvents().forEach(this::apply);
+    }
+
+    List<? extends Event> registerConfiguration(CassandraMailQueueViewConfiguration configuration) {
+        boolean isSame = state.maybeConfiguration.map(configuration::equals).orElse(false);
+        if (isSame) {
+            return EMPTY_EVENTS;
+        }
+
+        state.maybeConfiguration.ifPresent(oldConfiguration -> oldConfiguration.validateConfigurationChange(configuration));
+
+        return ImmutableList.of(new ConfigurationChanged(
+            aggregateId,
+            history.getNextEventId(),
+            configuration));
+    }
+
+    Optional<CassandraMailQueueViewConfiguration> getCurrentConfiguration() {
+        return state.maybeConfiguration;
+    }
+
+    private void apply(Event event) {
+        if (event instanceof ConfigurationChanged) {
+            ConfigurationChanged configurationChanged = (ConfigurationChanged) event;
+
+            state = state.set(configurationChanged.getConfiguration());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/032c08f0/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/ConfigurationChanged.java
----------------------------------------------------------------------
diff --git a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/ConfigurationChanged.java b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/ConfigurationChanged.java
new file mode 100644
index 0000000..7eb984b
--- /dev/null
+++ b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/ConfigurationChanged.java
@@ -0,0 +1,59 @@
+/****************************************************************
+ * 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.queue.rabbitmq.view.cassandra.configuration;
+
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.EventId;
+
+import com.google.common.base.Preconditions;
+
+class ConfigurationChanged implements Event {
+
+    private final EventId eventId;
+    private final AggregateId aggregateId;
+    private final CassandraMailQueueViewConfiguration configuration;
+
+    ConfigurationChanged(AggregateId aggregateId,
+                         EventId eventId,
+                         CassandraMailQueueViewConfiguration configuration) {
+        Preconditions.checkNotNull(aggregateId);
+        Preconditions.checkNotNull(eventId);
+        Preconditions.checkNotNull(configuration);
+
+        this.aggregateId = aggregateId;
+        this.eventId = eventId;
+        this.configuration = configuration;
+    }
+
+    CassandraMailQueueViewConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    @Override
+    public EventId eventId() {
+        return eventId;
+    }
+
+    @Override
+    public AggregateId getAggregateId() {
+        return aggregateId;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/032c08f0/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/EventsourcingConfigurationManagement.java
----------------------------------------------------------------------
diff --git a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/EventsourcingConfigurationManagement.java b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/EventsourcingConfigurationManagement.java
new file mode 100644
index 0000000..d3a9c5b
--- /dev/null
+++ b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/EventsourcingConfigurationManagement.java
@@ -0,0 +1,65 @@
+/****************************************************************
+ * 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.queue.rabbitmq.view.cassandra.configuration;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.EventSourcingSystem;
+import org.apache.james.eventsourcing.Subscriber;
+import org.apache.james.eventsourcing.eventstore.EventStore;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+
+public class EventsourcingConfigurationManagement {
+    static final String CONFIGURATION_AGGREGATE_KEY = "CassandraMailQueueViewConfiguration";
+    static final AggregateId CONFIGURATION_AGGREGATE_ID = () -> CONFIGURATION_AGGREGATE_KEY;
+
+    private static final ImmutableSet<Subscriber> NO_SUBSCRIBER = ImmutableSet.of();
+
+    private final EventStore eventStore;
+    private final EventSourcingSystem eventSourcingSystem;
+
+    @Inject
+    public EventsourcingConfigurationManagement(EventStore eventStore) {
+        this.eventSourcingSystem = new EventSourcingSystem(
+            ImmutableSet.of(new RegisterConfigurationCommandHandler(eventStore)),
+            NO_SUBSCRIBER,
+            eventStore);
+        this.eventStore = eventStore;
+    }
+
+    @VisibleForTesting
+    Optional<CassandraMailQueueViewConfiguration> load() {
+        return ConfigurationAggregate
+            .load(CONFIGURATION_AGGREGATE_ID, eventStore.getEventsOfAggregate(CONFIGURATION_AGGREGATE_ID))
+            .getCurrentConfiguration();
+    }
+
+    public void registerConfiguration(CassandraMailQueueViewConfiguration newConfiguration) {
+        Preconditions.checkNotNull(newConfiguration);
+
+        eventSourcingSystem.dispatch(new RegisterConfigurationCommand(newConfiguration, CONFIGURATION_AGGREGATE_ID));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/032c08f0/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommand.java
----------------------------------------------------------------------
diff --git a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommand.java b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommand.java
new file mode 100644
index 0000000..e35bb35
--- /dev/null
+++ b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommand.java
@@ -0,0 +1,64 @@
+/****************************************************************
+ * 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.queue.rabbitmq.view.cassandra.configuration;
+
+import java.util.Objects;
+
+import org.apache.james.eventsourcing.AggregateId;
+import org.apache.james.eventsourcing.Command;
+
+import com.google.common.base.Preconditions;
+
+class RegisterConfigurationCommand implements Command {
+
+    private final CassandraMailQueueViewConfiguration configuration;
+    private final AggregateId aggregateId;
+
+    RegisterConfigurationCommand(CassandraMailQueueViewConfiguration configuration, AggregateId aggregateId) {
+        Preconditions.checkNotNull(configuration);
+        Preconditions.checkNotNull(aggregateId);
+        this.aggregateId = aggregateId;
+        this.configuration = configuration;
+    }
+
+    CassandraMailQueueViewConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof RegisterConfigurationCommand) {
+            RegisterConfigurationCommand that = (RegisterConfigurationCommand) o;
+
+            return Objects.equals(this.configuration, that.configuration)
+                && Objects.equals(this.aggregateId, that.aggregateId);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(configuration, aggregateId);
+    }
+
+    public AggregateId getAggregateId() {
+        return aggregateId;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/032c08f0/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommandHandler.java
----------------------------------------------------------------------
diff --git a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommandHandler.java b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommandHandler.java
new file mode 100644
index 0000000..a679823
--- /dev/null
+++ b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommandHandler.java
@@ -0,0 +1,47 @@
+/****************************************************************
+ * 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.queue.rabbitmq.view.cassandra.configuration;
+
+import java.util.List;
+
+import org.apache.james.eventsourcing.CommandHandler;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.eventstore.EventStore;
+
+class RegisterConfigurationCommandHandler implements CommandHandler<RegisterConfigurationCommand> {
+
+    private final EventStore eventStore;
+
+    RegisterConfigurationCommandHandler(EventStore eventStore) {
+        this.eventStore = eventStore;
+    }
+
+    @Override
+    public Class<RegisterConfigurationCommand> handledClass() {
+        return RegisterConfigurationCommand.class;
+    }
+
+    @Override
+    public List<? extends Event> handle(RegisterConfigurationCommand command) {
+        return ConfigurationAggregate
+            .load(command.getAggregateId(), eventStore.getEventsOfAggregate(command.getAggregateId()))
+            .registerConfiguration(command.getConfiguration());
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/032c08f0/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/EventsourcingConfigurationManagementTest.java
----------------------------------------------------------------------
diff --git a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/EventsourcingConfigurationManagementTest.java b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/EventsourcingConfigurationManagementTest.java
new file mode 100644
index 0000000..fd5a272
--- /dev/null
+++ b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/EventsourcingConfigurationManagementTest.java
@@ -0,0 +1,247 @@
+/****************************************************************
+ * 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.queue.rabbitmq.view.cassandra.configuration;
+
+import static org.apache.james.queue.rabbitmq.view.cassandra.configuration.EventsourcingConfigurationManagement.CONFIGURATION_AGGREGATE_ID;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.time.Duration;
+import java.util.List;
+
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.eventstore.memory.InMemoryEventStore;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class EventsourcingConfigurationManagementTest {
+    private static final int DEFAULT_BUCKET_COUNT = 10;
+    private static final int DEFAULT_UPDATE_PACE = 100;
+    private static final Duration ONE_HOUR = Duration.ofHours(1);
+    private static final Duration TWO_HOURS = Duration.ofHours(2);
+    private static final Duration FORTY_FIVE_MINUTES = Duration.ofMinutes(45);
+    private static final Duration THIRTY_MINUTES = Duration.ofMinutes(30);
+
+    private static final CassandraMailQueueViewConfiguration FIRST_CONFIGURATION = CassandraMailQueueViewConfiguration.builder()
+        .bucketCount(DEFAULT_BUCKET_COUNT)
+        .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+        .sliceWindow(ONE_HOUR)
+        .build();
+    private static final CassandraMailQueueViewConfiguration SECOND_CONFIGURATION = CassandraMailQueueViewConfiguration.builder()
+        .bucketCount(DEFAULT_BUCKET_COUNT + 1)
+        .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+        .sliceWindow(ONE_HOUR)
+        .build();
+    private static final CassandraMailQueueViewConfiguration THIRD_CONFIGURATION = CassandraMailQueueViewConfiguration.builder()
+        .bucketCount(DEFAULT_BUCKET_COUNT + 2)
+        .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+        .sliceWindow(ONE_HOUR)
+        .build();
+    private InMemoryEventStore eventStore;
+
+    private EventsourcingConfigurationManagement createConfigurationManagement() {
+        return new EventsourcingConfigurationManagement(eventStore);
+    }
+
+    @BeforeEach
+    void setUp() {
+        eventStore = new InMemoryEventStore();
+    }
+
+    @Test
+    void loadShouldReturnEmptyIfNoConfigurationStored() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+
+        assertThat(testee.load())
+            .isEmpty();
+    }
+
+    @Test
+    void loadShouldReturnTheLastConfiguration() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+        testee.registerConfiguration(FIRST_CONFIGURATION);
+        testee.registerConfiguration(SECOND_CONFIGURATION);
+        testee.registerConfiguration(THIRD_CONFIGURATION);
+
+        assertThat(testee.load())
+            .contains(THIRD_CONFIGURATION);
+    }
+
+    @Test
+    void loadConfigurationShouldThrowWhenConfigurationIsNull() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+
+        assertThatThrownBy(() -> testee.registerConfiguration(null))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void loadConfigurationShouldThrowWhenBucketCountDecrease() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+        testee.registerConfiguration(CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+            .sliceWindow(ONE_HOUR)
+            .build());
+
+        assertThatThrownBy(() -> testee.registerConfiguration(CassandraMailQueueViewConfiguration.builder()
+                .bucketCount(DEFAULT_BUCKET_COUNT - 1)
+                .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+                .sliceWindow(ONE_HOUR)
+                .build()))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void loadConfigurationShouldUpdateStoredConfigurationWhenIncreaseBucketCount() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+        testee.registerConfiguration(CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+            .sliceWindow(ONE_HOUR)
+            .build());
+
+        CassandraMailQueueViewConfiguration increaseOneBucketConfiguration = CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT + 1)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+            .sliceWindow(ONE_HOUR)
+            .build();
+        testee.registerConfiguration(increaseOneBucketConfiguration);
+
+        assertThat(testee.load())
+            .contains(increaseOneBucketConfiguration);
+    }
+
+    @Test
+    void loadConfigurationShouldStoreConfiguration() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+
+        testee.registerConfiguration(FIRST_CONFIGURATION);
+
+        assertThat(testee.load())
+            .contains(FIRST_CONFIGURATION);
+    }
+
+    @Test
+    void loadConfigurationShouldThrowWhenIncreaseSliceWindow() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+        testee.registerConfiguration(CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+            .sliceWindow(ONE_HOUR)
+            .build());
+
+        assertThatThrownBy(() -> testee.registerConfiguration(CassandraMailQueueViewConfiguration.builder()
+                .bucketCount(DEFAULT_BUCKET_COUNT)
+                .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+                .sliceWindow(TWO_HOURS)
+                .build()))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void loadConfigurationShouldThrowWhenDecreaseSliceWindowByANotDivisibleNumber() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+        testee.registerConfiguration(CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+            .sliceWindow(ONE_HOUR)
+            .build());
+
+        assertThatThrownBy(() -> testee.registerConfiguration(CassandraMailQueueViewConfiguration.builder()
+                .bucketCount(DEFAULT_BUCKET_COUNT)
+                .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+                .sliceWindow(FORTY_FIVE_MINUTES)
+                .build()))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void loadConfigurationShouldUpdateConfigurationWhenDecreaseSliceWindowByADivisibleNumber() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+        testee.registerConfiguration(CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+            .sliceWindow(ONE_HOUR)
+            .build());
+
+        CassandraMailQueueViewConfiguration decreaseTwiceSliceWindowConfiguration = CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+            .sliceWindow(THIRTY_MINUTES)
+            .build();
+        testee.registerConfiguration(decreaseTwiceSliceWindowConfiguration);
+
+        assertThat(testee.load())
+            .contains(decreaseTwiceSliceWindowConfiguration);
+    }
+
+    @Test
+    void loadConfigurationShouldUpdateConfigurationWhenIncreaseUpdatePace() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+        testee.registerConfiguration(CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+            .sliceWindow(ONE_HOUR)
+            .build());
+
+        CassandraMailQueueViewConfiguration decreaseTwiceSliceWindowConfiguration = CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE + 10)
+            .sliceWindow(ONE_HOUR)
+            .build();
+        testee.registerConfiguration(decreaseTwiceSliceWindowConfiguration);
+
+        assertThat(testee.load())
+            .contains(decreaseTwiceSliceWindowConfiguration);
+    }
+
+    @Test
+    void loadConfigurationShouldUpdateConfigurationWhenDecreaseUpdatePace() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+        testee.registerConfiguration(CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE)
+            .sliceWindow(ONE_HOUR)
+            .build());
+
+        CassandraMailQueueViewConfiguration decreaseTwiceSliceWindowConfiguration = CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(DEFAULT_BUCKET_COUNT)
+            .updateBrowseStartPace(DEFAULT_UPDATE_PACE - 10)
+            .sliceWindow(ONE_HOUR)
+            .build();
+        testee.registerConfiguration(decreaseTwiceSliceWindowConfiguration);
+
+        assertThat(testee.load())
+            .contains(decreaseTwiceSliceWindowConfiguration);
+    }
+
+    @Test
+    void loadConfigurationShouldIgnoreDuplicateWhenStoreTheSameConfigurationTwice() {
+        EventsourcingConfigurationManagement testee = createConfigurationManagement();
+        testee.registerConfiguration(FIRST_CONFIGURATION);
+        testee.registerConfiguration(FIRST_CONFIGURATION);
+
+        List<Event> eventsStored = eventStore.getEventsOfAggregate(CONFIGURATION_AGGREGATE_ID)
+            .getEvents();
+        assertThat(eventsStored)
+            .hasSize(1);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/032c08f0/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommandTest.java
----------------------------------------------------------------------
diff --git a/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommandTest.java b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommandTest.java
new file mode 100644
index 0000000..c845413
--- /dev/null
+++ b/server/queue/queue-rabbitmq/src/test/java/org/apache/james/queue/rabbitmq/view/cassandra/configuration/RegisterConfigurationCommandTest.java
@@ -0,0 +1,55 @@
+/****************************************************************
+ * 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.queue.rabbitmq.view.cassandra.configuration;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.time.Duration;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class RegisterConfigurationCommandTest {
+
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(RegisterConfigurationCommand.class)
+            .verify();
+    }
+
+    @Test
+    void constructorShouldThrowWhenNullConfiguration() {
+        assertThatThrownBy(() -> new RegisterConfigurationCommand(null, () -> "agg"))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void constructorShouldThrowWhenNullAggregateId() {
+        CassandraMailQueueViewConfiguration configuration = CassandraMailQueueViewConfiguration.builder()
+            .bucketCount(2)
+            .updateBrowseStartPace(1000)
+            .sliceWindow(Duration.ofHours(1))
+            .build();
+
+        assertThatThrownBy(() -> new RegisterConfigurationCommand(configuration, null))
+            .isInstanceOf(NullPointerException.class);
+    }
+}
\ No newline at end of file


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