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 2019/12/19 10:38:35 UTC

[james-project] 01/14: JAMES-3012 Temporally duplicating classes for common JMAP used in tests

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 8d92187babf198a0430ab3183d4de920e322451d
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Fri Dec 13 10:41:09 2019 +0700

    JAMES-3012 Temporally duplicating classes for common JMAP used in tests
    
    More precise - integration tests. The will be an attempt to move some
    test class in jmap-integration-tests to where they should belong to -
    webadmin-integration-tests.
    
    To make it become smooth, I need to extract some common classes to a
    generic maven module which is james-server-testing.
    
    This is the first step, afterward is moving, and the last is cleaning
---
 server/testing/pom.xml                             |  17 +-
 .../java/org/apache/james/jmap/AccessToken.java    |  40 ++++
 .../apache/james/jmap/HttpJmapAuthentication.java  |  73 ++++++++
 .../apache/james/jmap/JMAPTestingConstants.java    |  68 +++++++
 .../org/apache/james/jmap/JmapCommonRequests.java  | 206 +++++++++++++++++++++
 .../java/org/apache/james/jmap/JmapURIBuilder.java |  35 ++++
 6 files changed, 435 insertions(+), 4 deletions(-)

diff --git a/server/testing/pom.xml b/server/testing/pom.xml
index b0d3e9a..d981af7 100644
--- a/server/testing/pom.xml
+++ b/server/testing/pom.xml
@@ -48,6 +48,14 @@
             <artifactId>guava</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.jayway.jsonpath</groupId>
+            <artifactId>json-path</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-net</groupId>
+            <artifactId>commons-net</artifactId>
+        </dependency>
+        <dependency>
             <groupId>io.rest-assured</groupId>
             <artifactId>rest-assured</artifactId>
         </dependency>
@@ -57,12 +65,13 @@
             <version>3.6.1</version>
         </dependency>
         <dependency>
-            <groupId>org.awaitility</groupId>
-            <artifactId>awaitility</artifactId>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>fluent-hc</artifactId>
+            <version>4.5.10</version>
         </dependency>
         <dependency>
-            <groupId>commons-net</groupId>
-            <artifactId>commons-net</artifactId>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
         </dependency>
         <dependency>
             <groupId>org.testcontainers</groupId>
diff --git a/server/testing/src/main/java/org/apache/james/jmap/AccessToken.java b/server/testing/src/main/java/org/apache/james/jmap/AccessToken.java
new file mode 100644
index 0000000..0e9969d
--- /dev/null
+++ b/server/testing/src/main/java/org/apache/james/jmap/AccessToken.java
@@ -0,0 +1,40 @@
+/****************************************************************
+ * 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.jmap;
+
+import com.google.common.annotations.VisibleForTesting;
+
+@VisibleForTesting
+public class AccessToken {
+
+    public static AccessToken of(String tokenString) {
+        return new AccessToken(tokenString);
+    }
+
+    private final String token;
+
+    private AccessToken(String token) {
+        this.token = token;
+    }
+
+    public String asString() {
+        return token;
+    }
+}
diff --git a/server/testing/src/main/java/org/apache/james/jmap/HttpJmapAuthentication.java b/server/testing/src/main/java/org/apache/james/jmap/HttpJmapAuthentication.java
new file mode 100644
index 0000000..79e14c9
--- /dev/null
+++ b/server/testing/src/main/java/org/apache/james/jmap/HttpJmapAuthentication.java
@@ -0,0 +1,73 @@
+/****************************************************************
+ * 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.jmap;
+
+import static org.apache.james.jmap.JMAPTestingConstants.calmlyAwait;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.client.fluent.Response;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.ContentType;
+import org.apache.james.core.Username;
+import org.hamcrest.core.IsAnything;
+
+import com.jayway.jsonpath.JsonPath;
+
+public class HttpJmapAuthentication {
+
+    public static AccessToken authenticateJamesUser(URIBuilder uriBuilder, Username username, String password) {
+        return calmlyAwait.until(
+            () -> doAuthenticate(uriBuilder, username, password), IsAnything.anything());
+    }
+
+    public static AccessToken doAuthenticate(URIBuilder uriBuilder, Username username, String password) throws ClientProtocolException, IOException, URISyntaxException {
+        String continuationToken = getContinuationToken(uriBuilder, username);
+
+        Response response = postAuthenticate(uriBuilder, password, continuationToken);
+
+        return AccessToken.of(
+            JsonPath.parse(response.returnContent().asString())
+                .read("accessToken"));
+    }
+
+    private static Response postAuthenticate(URIBuilder uriBuilder, String password, String continuationToken) throws ClientProtocolException, IOException, URISyntaxException {
+        return Request.Post(uriBuilder.setPath("/authentication").build())
+                .bodyString("{\"token\": \"" + continuationToken + "\", \"method\": \"password\", \"password\": \"" + password + "\"}", 
+                        ContentType.APPLICATION_JSON)
+                .setHeader("Accept", ContentType.APPLICATION_JSON.getMimeType())
+                .execute();
+    }
+
+    private static String getContinuationToken(URIBuilder uriBuilder, Username username) throws ClientProtocolException, IOException, URISyntaxException {
+        Response response = Request.Post(uriBuilder.setPath("/authentication").build())
+            .bodyString("{\"username\": \"" + username.asString() + "\", \"clientName\": \"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe Blogg’s iPhone\"}",
+                ContentType.APPLICATION_JSON)
+            .setHeader("Accept", ContentType.APPLICATION_JSON.getMimeType())
+            .execute();
+
+        return JsonPath.parse(response.returnContent().asString())
+            .read("continuationToken");
+    }
+
+}
diff --git a/server/testing/src/main/java/org/apache/james/jmap/JMAPTestingConstants.java b/server/testing/src/main/java/org/apache/james/jmap/JMAPTestingConstants.java
new file mode 100644
index 0000000..b2df5d3
--- /dev/null
+++ b/server/testing/src/main/java/org/apache/james/jmap/JMAPTestingConstants.java
@@ -0,0 +1,68 @@
+/****************************************************************
+ * 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.jmap;
+
+import static io.restassured.config.EncoderConfig.encoderConfig;
+import static io.restassured.config.RestAssuredConfig.newConfig;
+
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.james.core.Username;
+import org.awaitility.Awaitility;
+import org.awaitility.Duration;
+import org.awaitility.core.ConditionFactory;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.http.ContentType;
+
+public interface JMAPTestingConstants {
+    Duration slowPacedPollInterval = Duration.ONE_HUNDRED_MILLISECONDS;
+    Duration ONE_MILLISECOND = new Duration(1, TimeUnit.MILLISECONDS);
+
+    ConditionFactory calmlyAwait = Awaitility.with()
+        .pollInterval(slowPacedPollInterval)
+        .and().with()
+        .pollDelay(ONE_MILLISECOND)
+        .await();
+
+    RequestSpecBuilder jmapRequestSpecBuilder = new RequestSpecBuilder()
+        .setContentType(ContentType.JSON)
+        .setAccept(ContentType.JSON)
+        .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(StandardCharsets.UTF_8)));
+
+    String NAME = "[0][0]";
+    String ARGUMENTS = "[0][1]";
+    String FIRST_MAILBOX = ARGUMENTS + ".list[0]";
+    String SECOND_MAILBOX = ARGUMENTS + ".list[1]";
+    String SECOND_NAME = "[1][0]";
+    String SECOND_ARGUMENTS = "[1][1]";
+
+    String DOMAIN = "domain.tld";
+    Username BOB = Username.of("bob@" + DOMAIN);
+    String BOB_PASSWORD = "123456";
+    Username ALICE = Username.of("alice@" + DOMAIN);
+    String ALICE_PASSWORD = "789123";
+    Username CEDRIC = Username.of("cedric@" + DOMAIN);
+    String CEDRIC_PASSWORD = "456789";
+
+
+    String LOCALHOST_IP = "127.0.0.1";
+}
diff --git a/server/testing/src/main/java/org/apache/james/jmap/JmapCommonRequests.java b/server/testing/src/main/java/org/apache/james/jmap/JmapCommonRequests.java
new file mode 100644
index 0000000..3ed86ad
--- /dev/null
+++ b/server/testing/src/main/java/org/apache/james/jmap/JmapCommonRequests.java
@@ -0,0 +1,206 @@
+/****************************************************************
+ * 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.jmap;
+
+import static io.restassured.RestAssured.with;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasKey;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.text.IsEmptyString.emptyOrNullString;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.james.mailbox.Role;
+import org.apache.james.mailbox.model.MailboxId;
+
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.path.json.JsonPath;
+import io.restassured.specification.ResponseSpecification;
+
+public class JmapCommonRequests {
+    private static final String NAME = "[0][0]";
+    private static final String ARGUMENTS = "[0][1]";
+    private static final String NOT_UPDATED = ARGUMENTS + ".notUpdated";
+
+    public static String getOutboxId(AccessToken accessToken) {
+        return getMailboxId(accessToken, Role.OUTBOX);
+    }
+
+    public static String getDraftId(AccessToken accessToken) {
+        return getMailboxId(accessToken, Role.DRAFTS);
+    }
+
+    public static String getMailboxId(AccessToken accessToken, Role role) {
+        return getAllMailboxesIds(accessToken).stream()
+            .filter(mailbox -> mailbox.get("role").equals(role.serialize()))
+            .map(mailbox -> mailbox.get("id"))
+            .findFirst().get();
+    }
+
+    public static List<Map<String, String>> getAllMailboxesIds(AccessToken accessToken) {
+        return with()
+            .header("Authorization", accessToken.asString())
+            .body("[[\"getMailboxes\", {\"properties\": [\"role\", \"name\", \"id\"]}, \"#0\"]]")
+            .post("/jmap")
+        .andReturn()
+            .body()
+            .jsonPath()
+            .getList(ARGUMENTS + ".list");
+    }
+
+    public static boolean isAnyMessageFoundInRecipientsMailboxes(AccessToken recipientToken) {
+        try {
+            with()
+                .header("Authorization", recipientToken.asString())
+                .body("[[\"getMessageList\", {}, \"#0\"]]")
+            .when()
+                .post("/jmap")
+            .then()
+                .statusCode(200)
+                .body(NAME, equalTo("messageList"))
+                .body(ARGUMENTS + ".messageIds", hasSize(1));
+            return true;
+
+        } catch (AssertionError e) {
+            return false;
+        }
+    }
+
+    public static boolean isAnyMessageFoundInRecipientsMailbox(AccessToken recipientToken, MailboxId mailboxId) {
+        try {
+            with()
+                .header("Authorization", recipientToken.asString())
+                .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + mailboxId.serialize() + "\"]}}, \"#0\"]]")
+            .when()
+                .post("/jmap")
+            .then()
+                .statusCode(200)
+                .body(NAME, equalTo("messageList"))
+                .body(ARGUMENTS + ".messageIds", hasSize(1));
+            return true;
+
+        } catch (AssertionError e) {
+            return false;
+        }
+    }
+
+    public static String getInboxId(AccessToken accessToken) {
+        return getMailboxId(accessToken, Role.INBOX);
+    }
+
+    public static List<String> listMessageIdsForAccount(AccessToken accessToken) {
+        return with()
+                .header("Authorization", accessToken.asString())
+                .body("[[\"getMessageList\", {}, \"#0\"]]")
+                .post("/jmap")
+            .then()
+                .extract()
+                .body()
+                .path(ARGUMENTS + ".messageIds");
+    }
+
+    public static String getLastMessageId(AccessToken accessToken) {
+        return with()
+                .header("Authorization", accessToken.asString())
+                .body("[[\"getMessageList\", {\"sort\":[\"date desc\"]}, \"#0\"]]")
+                .post("/jmap")
+            .then()
+                .extract()
+                .body()
+                .path(ARGUMENTS + ".messageIds[0]");
+    }
+
+    public static String getLatestMessageId(AccessToken accessToken, Role mailbox) {
+        String inboxId = getMailboxId(accessToken, mailbox);
+        return with()
+                .header("Authorization", accessToken.asString())
+                .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + inboxId + "\"]}, \"sort\":[\"date desc\"]}, \"#0\"]]")
+                .post("/jmap")
+            .then()
+                .extract()
+                .path(ARGUMENTS + ".messageIds[0]");
+    }
+
+    public static String bodyOfMessage(AccessToken accessToken, String messageId) {
+        return getMessageContent(accessToken, messageId)
+                .get(ARGUMENTS + ".list[0].textBody");
+    }
+
+    public static List<String> receiversOfMessage(AccessToken accessToken, String messageId) {
+        return getMessageContent(accessToken, messageId)
+                .getList(ARGUMENTS + ".list[0].to.email");
+    }
+
+    private static JsonPath getMessageContent(AccessToken accessToken, String messageId) {
+        return with()
+                .header("Authorization", accessToken.asString())
+                .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]")
+            .when()
+                .post("/jmap")
+            .then()
+                .statusCode(200)
+                .body(NAME, equalTo("messages"))
+                .body(ARGUMENTS + ".list", hasSize(1))
+            .extract()
+                .jsonPath();
+    }
+
+    public static List<String> listMessageIdsInMailbox(AccessToken accessToken, String mailboxId) {
+        return with()
+                .header("Authorization", accessToken.asString())
+                .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + mailboxId + "\"]}}, \"#0\"]]")
+                .post("/jmap")
+            .then()
+                .extract()
+                .body()
+                .path(ARGUMENTS + ".messageIds");
+    }
+
+    public static ResponseSpecification getSetMessagesUpdateOKResponseAssertions(String messageId) {
+        ResponseSpecBuilder builder = new ResponseSpecBuilder()
+            .expectStatusCode(200)
+            .expectBody(NAME, equalTo("messagesSet"))
+            .expectBody(ARGUMENTS + ".updated", hasSize(1))
+            .expectBody(ARGUMENTS + ".updated", contains(messageId))
+            .expectBody(ARGUMENTS + ".error", is(emptyOrNullString()))
+            .expectBody(NOT_UPDATED, not(hasKey(messageId)));
+        return builder.build();
+    }
+
+    public static void deleteMessages(AccessToken accessToken, List<String> idsToDestroy) {
+        String idString = concatMessageIds(idsToDestroy);
+
+        with()
+            .header("Authorization", accessToken.asString())
+            .body("[[\"setMessages\", {\"destroy\": [" + idString + "]}, \"#0\"]]")
+            .post("/jmap");
+    }
+
+    public static String concatMessageIds(List<String> ids) {
+        return ids.stream()
+            .map(id -> "\"" + id + "\"")
+            .collect(Collectors.joining(","));
+    }
+}
diff --git a/server/testing/src/main/java/org/apache/james/jmap/JmapURIBuilder.java b/server/testing/src/main/java/org/apache/james/jmap/JmapURIBuilder.java
new file mode 100644
index 0000000..aed6767
--- /dev/null
+++ b/server/testing/src/main/java/org/apache/james/jmap/JmapURIBuilder.java
@@ -0,0 +1,35 @@
+/****************************************************************
+ * 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.jmap;
+
+import java.nio.charset.StandardCharsets;
+
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.james.util.Port;
+
+public class JmapURIBuilder {
+
+    public static URIBuilder baseUri(Port jmapPort) {
+        return new URIBuilder()
+            .setScheme("http")
+            .setHost("localhost")
+            .setPort(jmapPort.getValue())
+            .setCharset(StandardCharsets.UTF_8);
+    }
+}


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