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