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-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org