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/01/24 08:04:12 UTC

[03/11] james-project git commit: JAMES-2293 Webadmin should allow to list mail in repositories

JAMES-2293 Webadmin should allow to list mail in repositories


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

Branch: refs/heads/master
Commit: d423ef05367684d6827a1297f48fdf6db14e6d92
Parents: b773ace
Author: benwa <bt...@linagora.com>
Authored: Tue Jan 23 15:54:31 2018 +0700
Committer: benwa <bt...@linagora.com>
Committed: Wed Jan 24 14:58:37 2018 +0700

----------------------------------------------------------------------
 .../org/apache/james/util/streams/Limit.java    |   5 +
 .../org/apache/james/webadmin/dto/MailKey.java  |  50 +++++++
 .../webadmin/routes/MailRepositoriesRoutes.java |  84 +++++++++++
 .../service/MailRepositoryStoreService.java     |  14 ++
 .../routes/MailRepositoriesRoutesTest.java      | 148 ++++++++++++++++++-
 .../service/MailRepositoryStoreServiceTest.java |  62 ++++++++
 6 files changed, 359 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/d423ef05/server/container/util-java8/src/main/java/org/apache/james/util/streams/Limit.java
----------------------------------------------------------------------
diff --git a/server/container/util-java8/src/main/java/org/apache/james/util/streams/Limit.java b/server/container/util-java8/src/main/java/org/apache/james/util/streams/Limit.java
index b3ba512..268ed5e 100644
--- a/server/container/util-java8/src/main/java/org/apache/james/util/streams/Limit.java
+++ b/server/container/util-java8/src/main/java/org/apache/james/util/streams/Limit.java
@@ -35,6 +35,11 @@ public class Limit {
         }
     }
 
+    public static Limit from(Optional<Integer> limit) {
+        return limit.map(Limit::from)
+            .orElse(unlimited());
+    }
+
     public static Limit unlimited() {
         return new Limit(Optional.empty());
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/d423ef05/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKey.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKey.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKey.java
new file mode 100644
index 0000000..9d44dae
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/dto/MailKey.java
@@ -0,0 +1,50 @@
+/****************************************************************
+ * 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.webadmin.dto;
+
+import java.util.Objects;
+
+public class MailKey {
+
+    private final String repository;
+
+    public MailKey(String mailKey) {
+        this.repository = mailKey;
+    }
+
+    public String getMailKey() {
+        return repository;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof MailKey) {
+            MailKey mailKey = (MailKey) o;
+
+            return Objects.equals(this.repository, mailKey.repository);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(repository);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d423ef05/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
index dbfc60d..1cc7a06 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/routes/MailRepositoriesRoutes.java
@@ -19,22 +19,34 @@
 
 package org.apache.james.webadmin.routes;
 
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
+import java.util.Optional;
 
 import javax.inject.Inject;
+import javax.mail.MessagingException;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 
+import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.util.streams.Limit;
 import org.apache.james.webadmin.Routes;
 import org.apache.james.webadmin.service.MailRepositoryStoreService;
+import org.apache.james.webadmin.utils.ErrorResponder;
 import org.apache.james.webadmin.utils.JsonTransformer;
 import org.eclipse.jetty.http.HttpStatus;
 
+import com.google.common.base.Strings;
+
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
+import spark.Request;
 import spark.Service;
 
 @Api(tags = "MailRepositories")
@@ -43,6 +55,7 @@ import spark.Service;
 public class MailRepositoriesRoutes implements Routes {
 
     public static final String MAIL_REPOSITORIES = "mailRepositories";
+    public static final int NO_OFFSET = 0;
 
     private final JsonTransformer jsonTransformer;
     private final MailRepositoryStoreService repositoryStoreService;
@@ -59,6 +72,50 @@ public class MailRepositoriesRoutes implements Routes {
         this.service = service;
 
         defineGetMailRepositories();
+
+        defineListMails();
+    }
+
+    @GET
+    @Path("/{encodedUrl}/mails")
+    @ApiOperation(value = "Listing all mails in a repository")
+    @ApiImplicitParams({
+        @ApiImplicitParam(
+            required = false,
+            paramType = "query parameter",
+            dataType = "Integer",
+            defaultValue = "0",
+            example = "?offset=100",
+            value = "If present, skips the given number of key in the output."),
+        @ApiImplicitParam(
+            required = false,
+            paramType = "query parameter",
+            dataType = "Integer",
+            defaultValue = "0",
+            example = "?limit=100",
+            value = "If present, fixes the maximal number of key returned in that call.")
+    })
+    @ApiResponses(value = {
+        @ApiResponse(code = HttpStatus.OK_200, message = "The list of all mails in a repository", response = List.class),
+        @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Internal server error - Something went bad on the server side."),
+        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Bad request - invalid parameter")
+    })
+    public void defineListMails() {
+        service.get(MAIL_REPOSITORIES + "/:encodedUrl/mails", (request, response) -> {
+            int offset = asInteger(request, "offset").orElse(NO_OFFSET);
+            Limit limit = Limit.from(asInteger(request, "limit"));
+            String url = URLDecoder.decode(request.params("encodedUrl"), StandardCharsets.UTF_8.displayName());
+            try {
+                return repositoryStoreService.listMails(url, offset, limit);
+            } catch (MailRepositoryStore.MailRepositoryStoreException| MessagingException e) {
+                throw ErrorResponder.builder()
+                    .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500)
+                    .type(ErrorResponder.ErrorType.SERVER_ERROR)
+                    .cause(e)
+                    .message("Error while listing keys")
+                    .haltError();
+            }
+        }, jsonTransformer);
     }
 
     @GET
@@ -73,4 +130,31 @@ public class MailRepositoriesRoutes implements Routes {
             return repositoryStoreService.listMailRepositories();
         }, jsonTransformer);
     }
+
+    private Optional<Integer> asInteger(Request request, String parameterName) {
+        try {
+            return Optional.ofNullable(request.queryParams(parameterName))
+                .filter(s -> !Strings.isNullOrEmpty(s))
+                .map(Integer::valueOf)
+                .map(value -> keepPositive(value, parameterName));
+        } catch (NumberFormatException e) {
+            throw ErrorResponder.builder()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                .cause(e)
+                .message("Can not parse " + parameterName)
+                .haltError();
+        }
+    }
+
+    private int keepPositive(int value, String parameterName) {
+        if (value < 0) {
+            throw ErrorResponder.builder()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                .message(parameterName + " can not be negative")
+                .haltError();
+        }
+        return value;
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/d423ef05/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
index bb7f6cd..8be5c44 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/main/java/org/apache/james/webadmin/service/MailRepositoryStoreService.java
@@ -22,8 +22,13 @@ package org.apache.james.webadmin.service;
 import java.util.List;
 
 import javax.inject.Inject;
+import javax.mail.MessagingException;
 
+import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.util.streams.Iterators;
+import org.apache.james.util.streams.Limit;
+import org.apache.james.webadmin.dto.MailKey;
 import org.apache.james.webadmin.dto.MailRepositoryResponse;
 
 import com.github.steveash.guavate.Guavate;
@@ -43,4 +48,13 @@ public class MailRepositoryStoreService {
             .collect(Guavate.toImmutableList());
     }
 
+    public List<MailKey> listMails(String url, long offset, Limit limit) throws MailRepositoryStore.MailRepositoryStoreException, MessagingException {
+        MailRepository mailRepository = mailRepositoryStore.select(url);
+        return limit.applyOnStream(
+            Iterators.toStream(mailRepository.list())
+                .skip(offset))
+            .map(MailKey::new)
+            .collect(Guavate.toImmutableList());
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/d423ef05/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
index 7788b98..ddac73d 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
@@ -24,6 +24,8 @@ import static com.jayway.restassured.config.EncoderConfig.encoderConfig;
 import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
 import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
 import static org.mockito.Mockito.mock;
@@ -33,11 +35,14 @@ import java.nio.charset.StandardCharsets;
 import java.util.List;
 
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.memory.MemoryMailRepository;
 import org.apache.james.metrics.logger.DefaultMetricFactory;
 import org.apache.james.webadmin.WebAdminServer;
 import org.apache.james.webadmin.WebAdminUtils;
 import org.apache.james.webadmin.service.MailRepositoryStoreService;
+import org.apache.james.webadmin.utils.ErrorResponder;
 import org.apache.james.webadmin.utils.JsonTransformer;
+import org.apache.mailet.base.test.FakeMail;
 import org.eclipse.jetty.http.HttpStatus;
 import org.junit.After;
 import org.junit.Before;
@@ -50,12 +55,17 @@ import com.jayway.restassured.http.ContentType;
 
 public class MailRepositoriesRoutesTest {
 
+    public static final String URL_MY_REPO = "url://myRepo";
+    public static final String URL_ESCAPED_MY_REPO = "url%3A%2F%2FmyRepo";
+    public static final String MY_REPO_MAILS = "url%3A%2F%2FmyRepo/mails";
     private WebAdminServer webAdminServer;
     private MailRepositoryStore mailRepositoryStore;
+    private MemoryMailRepository mailRepository;
 
     @Before
     public void setUp() throws Exception {
         mailRepositoryStore = mock(MailRepositoryStore.class);
+        mailRepository = new MemoryMailRepository();
 
         webAdminServer = WebAdminUtils.createWebAdminServer(
                 new DefaultMetricFactory(),
@@ -96,20 +106,20 @@ public class MailRepositoriesRoutesTest {
     @Test
     public void getMailRepositoriesShouldReturnRepositoryWhenOne() {
         when(mailRepositoryStore.getUrls())
-            .thenReturn(ImmutableList.of("url://myRepo"));
+            .thenReturn(ImmutableList.of(URL_MY_REPO));
 
         when()
             .get()
         .then()
             .statusCode(HttpStatus.OK_200)
             .body("", hasSize(1))
-            .body("[0].repository", is("url://myRepo"))
-            .body("[0].encodedUrl", is("url%3A%2F%2FmyRepo"));
+            .body("[0].repository", is(URL_MY_REPO))
+            .body("[0].encodedUrl", is(URL_ESCAPED_MY_REPO));
     }
 
     @Test
     public void getMailRepositoriesShouldReturnTwoRepositoriesWhenTwo() {
-        ImmutableList<String> myRepositories = ImmutableList.of("url://myRepo", "url://mySecondRepo");
+        ImmutableList<String> myRepositories = ImmutableList.of(URL_MY_REPO, "url://mySecondRepo");
         when(mailRepositoryStore.getUrls())
             .thenReturn(myRepositories);
 
@@ -127,4 +137,134 @@ public class MailRepositoriesRoutesTest {
         assertThat(mailRepositories).containsOnlyElementsOf(myRepositories);
     }
 
+    @Test
+    public void listingKeysShouldReturnEmptyWhenNoMail() throws Exception {
+        when(mailRepositoryStore.select(URL_MY_REPO)).thenReturn(mailRepository);
+
+        when()
+            .get(MY_REPO_MAILS)
+        .then()
+            .statusCode(HttpStatus.OK_200)
+            .body("", hasSize(0));
+    }
+
+    @Test
+    public void listingKeysShouldReturnContainedKeys() throws Exception {
+        when(mailRepositoryStore.select(URL_MY_REPO)).thenReturn(mailRepository);
+
+        mailRepository.store(FakeMail.builder()
+            .name("name1")
+            .build());
+        mailRepository.store(FakeMail.builder()
+            .name("name2")
+            .build());
+
+        when()
+            .get(MY_REPO_MAILS)
+        .then()
+            .statusCode(HttpStatus.OK_200)
+            .body("", hasSize(2))
+            .body("mailKey", containsInAnyOrder("name1", "name2"));
+    }
+
+    @Test
+    public void listingKeysShouldApplyLimitAndOffset() throws Exception {
+        when(mailRepositoryStore.select(URL_MY_REPO)).thenReturn(mailRepository);
+
+        mailRepository.store(FakeMail.builder()
+            .name("name1")
+            .build());
+        mailRepository.store(FakeMail.builder()
+            .name("name2")
+            .build());
+        mailRepository.store(FakeMail.builder()
+            .name("name3")
+            .build());
+
+        when()
+            .get(MY_REPO_MAILS + "?offset=1&limit=1")
+        .then()
+            .statusCode(HttpStatus.OK_200)
+            .body("", hasSize(1))
+            .body("mailKey", containsInAnyOrder("name2"));
+    }
+
+    @Test
+    public void listingKeysShouldHandleErrorGracefully() throws Exception {
+        when(mailRepositoryStore.select(URL_MY_REPO))
+            .thenThrow(new MailRepositoryStore.MailRepositoryStoreException("message"));
+
+        when()
+            .get(MY_REPO_MAILS)
+        .then()
+            .statusCode(HttpStatus.INTERNAL_SERVER_ERROR_500)
+            .body("statusCode", is(500))
+            .body("type", is(ErrorResponder.ErrorType.SERVER_ERROR.getType()))
+            .body("message", is("Error while listing keys"))
+            .body("cause", containsString("message"));
+    }
+
+    @Test
+    public void listingKeysShouldReturnErrorOnInvalidOffset() throws Exception {
+        when()
+            .get(MY_REPO_MAILS + "?offset=invalid")
+        .then()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .body("statusCode", is(400))
+            .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+            .body("message", is("Can not parse offset"));
+    }
+
+    @Test
+    public void listingKeysShouldReturnErrorOnNegativeOffset() throws Exception {
+        when()
+            .get(MY_REPO_MAILS + "?offset=-1")
+        .then()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .body("statusCode", is(400))
+            .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+            .body("message", is("offset can not be negative"));
+    }
+
+    @Test
+    public void listingKeysShouldReturnErrorOnInvalidLimit() throws Exception {
+        when()
+            .get(MY_REPO_MAILS + "?limit=invalid")
+        .then()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .body("statusCode", is(400))
+            .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+            .body("message", is("Can not parse limit"));
+    }
+
+    @Test
+    public void listingKeysShouldReturnErrorOnNegativeLimit() throws Exception {
+        when()
+            .get(MY_REPO_MAILS + "?limit=-1")
+        .then()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .body("statusCode", is(400))
+            .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+            .body("message", is("limit can not be negative"));
+    }
+
+    @Test
+    public void listingKeysShouldIgnoreZeroedOffset() throws Exception {
+        when(mailRepositoryStore.select(URL_MY_REPO)).thenReturn(mailRepository);
+
+        mailRepository.store(FakeMail.builder()
+            .name("name1")
+            .build());
+        mailRepository.store(FakeMail.builder()
+            .name("name2")
+            .build());
+
+        when()
+            .get(MY_REPO_MAILS + "?offset=0")
+        .then()
+            .statusCode(HttpStatus.OK_200)
+            .body("", hasSize(2))
+            .body("mailKey", containsInAnyOrder("name1", "name2"));
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/d423ef05/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/MailRepositoryStoreServiceTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/MailRepositoryStoreServiceTest.java b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/MailRepositoryStoreServiceTest.java
index 11305f3..9079ae6 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/MailRepositoryStoreServiceTest.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/MailRepositoryStoreServiceTest.java
@@ -19,11 +19,17 @@
 package org.apache.james.webadmin.service;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import org.apache.james.mailrepository.api.MailRepositoryStore;
+import org.apache.james.mailrepository.memory.MemoryMailRepository;
+import org.apache.james.util.streams.Limit;
+import org.apache.james.webadmin.dto.MailKey;
 import org.apache.james.webadmin.dto.MailRepositoryResponse;
+import org.apache.james.webadmin.routes.MailRepositoriesRoutes;
+import org.apache.mailet.base.test.FakeMail;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -32,12 +38,17 @@ import com.google.common.collect.ImmutableList;
 public class MailRepositoryStoreServiceTest {
     private static final String FIRST_REPOSITORY = "url://repository";
     private static final String SECOND_REPOSITORY = "url://repository2";
+    private static final String NAME_1 = "name1";
+    private static final String NAME_2 = "name2";
+
     private MailRepositoryStore mailRepositoryStore;
     private MailRepositoryStoreService testee;
+    private MemoryMailRepository repository;
 
     @Before
     public void setUp() {
         mailRepositoryStore = mock(MailRepositoryStore.class);
+        repository = new MemoryMailRepository();
         testee = new MailRepositoryStoreService(mailRepositoryStore);
     }
 
@@ -63,4 +74,55 @@ public class MailRepositoryStoreServiceTest {
             .extracting(MailRepositoryResponse::getRepository)
             .containsOnly(FIRST_REPOSITORY, SECOND_REPOSITORY);
     }
+
+    @Test
+    public void listMailsShouldThrowWhenMailRepositoryStoreThrows() throws Exception {
+        when(mailRepositoryStore.select(FIRST_REPOSITORY))
+            .thenThrow(new MailRepositoryStore.MailRepositoryStoreException("message"));
+
+        assertThatThrownBy(() -> testee.listMails(FIRST_REPOSITORY, MailRepositoriesRoutes.NO_OFFSET, Limit.unlimited()))
+            .isInstanceOf(MailRepositoryStore.MailRepositoryStoreException.class);
+    }
+
+    @Test
+    public void listMailsShouldReturnEmptyWhenMailRepositoryIsEmpty() throws Exception {
+        when(mailRepositoryStore.select(FIRST_REPOSITORY)).thenReturn(repository);
+
+        assertThat(testee.listMails(FIRST_REPOSITORY, MailRepositoriesRoutes.NO_OFFSET, Limit.unlimited()))
+            .isEmpty();
+    }
+
+    @Test
+    public void listMailsShouldReturnContainedMailKeys() throws Exception {
+        when(mailRepositoryStore.select(FIRST_REPOSITORY)).thenReturn(repository);
+
+        repository.store(FakeMail.builder()
+            .name(NAME_1)
+            .build());
+        repository.store(FakeMail.builder()
+            .name(NAME_2)
+            .build());
+
+        assertThat(testee.listMails(FIRST_REPOSITORY, MailRepositoriesRoutes.NO_OFFSET, Limit.unlimited()))
+            .containsOnly(new MailKey(NAME_1), new MailKey(NAME_2));
+    }
+
+    @Test
+    public void listMailsShouldApplyLimitAndOffset() throws Exception {
+        when(mailRepositoryStore.select(FIRST_REPOSITORY)).thenReturn(repository);
+
+        repository.store(FakeMail.builder()
+            .name(NAME_1)
+            .build());
+        repository.store(FakeMail.builder()
+            .name(NAME_2)
+            .build());
+        repository.store(FakeMail.builder()
+            .name("name3")
+            .build());
+
+        int offset = 1;
+        assertThat(testee.listMails(FIRST_REPOSITORY, offset, Limit.from(1)))
+            .containsOnly(new MailKey(NAME_2));
+    }
 }


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