You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2021/02/03 02:51:14 UTC
[james-project] 08/12: JAMES-3491 WIP write tests for RFC-8887 JMAP
over websocket support
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 34f1a6888b26bd3d86edcbc46b06d5c8d28786e8
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Jan 28 17:25:10 2021 +0700
JAMES-3491 WIP write tests for RFC-8887 JMAP over websocket support
Exercise only the transport layer, no PUSH support yet.
---
.../jmap-rfc-8621-integration-tests-common/pom.xml | 9 +
.../jmap/rfc8621/contract/WebSocketContract.scala | 225 +++++++++++++++++++--
2 files changed, 215 insertions(+), 19 deletions(-)
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
index 539a520..b84b5f6 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/pom.xml
@@ -40,6 +40,10 @@
</dependency>
<dependency>
<groupId>${james.groupId}</groupId>
+ <artifactId>james-server-jmap-draft</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${james.groupId}</groupId>
<artifactId>testing-base</artifactId>
</dependency>
<dependency>
@@ -58,6 +62,11 @@
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.java-websocket</groupId>
+ <artifactId>Java-WebSocket</artifactId>
+ <version>1.5.1</version>
+ </dependency>
</dependencies>
<build>
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/WebSocketContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/WebSocketContract.scala
index ae91a42..73ea439 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/WebSocketContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/WebSocketContract.scala
@@ -18,10 +18,18 @@
****************************************************************/
package org.apache.james.jmap.rfc8621.contract
+import java.net.URI
+import java.util
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
import org.apache.james.GuiceJamesServer
+import org.apache.james.jmap.draft.JmapGuiceProbe
import org.apache.james.jmap.rfc8621.contract.Fixture._
import org.apache.james.jmap.rfc8621.contract.tags.CategoryTags
import org.apache.james.utils.DataProbeImpl
+import org.assertj.core.api.Assertions.assertThat
+import org.java_websocket.client.WebSocketClient
+import org.java_websocket.handshake.ServerHandshake
import org.junit.jupiter.api.{BeforeEach, Tag, Test}
trait WebSocketContract {
@@ -33,51 +41,230 @@ trait WebSocketContract {
.addUser(BOB.asString(), BOB_PASSWORD)
}
+ class ExampleClient(uri: URI) extends WebSocketClient(uri) {
+ val receivedResponses: util.LinkedList[String] = new util.LinkedList[String]()
+ var closeCode: Option[Integer] = None
+ var closeString: Option[String] = None
+
+ override def onOpen(serverHandshake: ServerHandshake): Unit = {
+ println(s"handshake ${serverHandshake.getHttpStatus}")
+ }
+
+ override def onMessage(s: String): Unit = {
+ println(s"Received: $s")
+ receivedResponses.add(s)
+ }
+
+ override def onClose(i: Int, s: String, b: Boolean): Unit = {
+ closeCode = Some(i)
+ closeString = Some(s)
+ println(s"Closing connection $i $s $b")
+ }
+
+ override def onError(e: Exception): Unit = {
+ println("Error: " + e.getMessage)
+ }
+ }
+
@Test
@Tag(CategoryTags.BASIC_FEATURE)
- def apiRequestsShouldBeProcessed(): Unit = {
- /*
- * TODO test an echo response - request (success)
- * */
+ def apiRequestsShouldBeProcessed(server: GuiceJamesServer): Unit = {
+ println("started")
+ val port = server.getProbe(classOf[JmapGuiceProbe])
+ .getJmapPort
+ .getValue
+ val client = new ExampleClient(new URI(s"ws://127.0.0.1:$port/jmap/ws"))
+ client.addHeader("Authorization", "Basic Ym9iQGRvbWFpbi50bGQ6Ym9icGFzc3dvcmQ=")
+ client.addHeader("Accept", ACCEPT_RFC8621_VERSION_HEADER)
+
+ client.connectBlocking()
+
+ Thread.sleep(500)
+
+ client.send("""{
+ | "@type": "Request",
+ | "requestId": "req-36",
+ | "using": [ "urn:ietf:params:jmap:core"],
+ | "methodCalls": [
+ | [
+ | "Core/echo",
+ | {
+ | "arg1": "arg1data",
+ | "arg2": "arg2data"
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+
+ Thread.sleep(500)
+
+
+ assertThat(client.receivedResponses).hasSize(1)
+ assertThatJson(client.receivedResponses.get(0)).isEqualTo(
+ """
+ |{
+ | "@type":"Response",
+ | "requestId":"req-36",
+ | "sessionState":"2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "methodResponses":[["Core/echo",{"arg1":"arg1data","arg2":"arg2data"},"c1"]]
+ |}
+ |""".stripMargin)
}
@Test
- def nonJsonPayloadShouldTriggerError(): Unit = {
- /*
- * TODO send 'the quick brown fox' and get an error level error
- * */
+ def apiRequestsShouldBeProcessedWhenNoRequestId(server: GuiceJamesServer): Unit = {
+ println("started")
+ val port = server.getProbe(classOf[JmapGuiceProbe])
+ .getJmapPort
+ .getValue
+ val client = new ExampleClient(new URI(s"ws://127.0.0.1:$port/jmap/ws"))
+ client.addHeader("Authorization", "Basic Ym9iQGRvbWFpbi50bGQ6Ym9icGFzc3dvcmQ=")
+ client.addHeader("Accept", ACCEPT_RFC8621_VERSION_HEADER)
+
+ client.connectBlocking()
+
+ Thread.sleep(500)
+
+ client.send("""{
+ | "@type": "Request",
+ | "using": [ "urn:ietf:params:jmap:core"],
+ | "methodCalls": [
+ | [
+ | "Core/echo",
+ | {
+ | "arg1": "arg1data",
+ | "arg2": "arg2data"
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+
+ Thread.sleep(500)
+
+
+ assertThat(client.receivedResponses).hasSize(1)
+ assertThatJson(client.receivedResponses.get(0)).isEqualTo(
+ """
+ |{
+ | "@type":"Response",
+ | "requestId":null,
+ | "sessionState":"2c9f1b12-b35a-43e6-9af2-0106fb53a943",
+ | "methodResponses":[["Core/echo",{"arg1":"arg1data","arg2":"arg2data"},"c1"]]
+ |}
+ |""".stripMargin)
}
@Test
- def handshakeShouldBeAuthenticated(): Unit = {
- /*
- * TODO set up no auth
- * */
+ def nonJsonPayloadShouldTriggerError(server: GuiceJamesServer): Unit = {
+ println("started")
+ val port = server.getProbe(classOf[JmapGuiceProbe])
+ .getJmapPort
+ .getValue
+ val client = new ExampleClient(new URI(s"ws://127.0.0.1:$port/jmap/ws"))
+ client.addHeader("Authorization", "Basic Ym9iQGRvbWFpbi50bGQ6Ym9icGFzc3dvcmQ=")
+ client.addHeader("Accept", ACCEPT_RFC8621_VERSION_HEADER)
+
+ client.connectBlocking()
+
+ Thread.sleep(500)
+
+ client.send("The quick brown fox".stripMargin)
+
+ Thread.sleep(500)
+
+ assertThat(client.receivedResponses).hasSize(1)
+ assertThatJson(client.receivedResponses.get(0)).isEqualTo(
+ """
+ |{
+ | "status":400,
+ | "detail":"The request was successfully parsed as JSON but did not match the type signature of the Request object: List((,List(JsonValidationError(List(Unrecognized token 'The': was expecting ('true', 'false' or 'null')\n at [Source: (String)\"The quick brown fox\"; line: 1, column: 4]),ArraySeq()))))",
+ | "type":"urn:ietf:params:jmap:error:notRequest",
+ | "requestId":null,
+ | "@type":"RequestError"
+ |}
+ |""".stripMargin)
}
@Test
- def noTypeFiledShouldTriggerError(): Unit = {
- /*
- * TODO send something without @type and get an error level error
- * */
+ def handshakeShouldBeAuthenticated(server: GuiceJamesServer): Unit = {
+ val port = server.getProbe(classOf[JmapGuiceProbe])
+ .getJmapPort
+ .getValue
+ val client = new ExampleClient(new URI(s"ws://127.0.0.1:$port/jmap/ws"))
+ client.addHeader("Accept", ACCEPT_RFC8621_VERSION_HEADER)
+
+ client.connectBlocking()
+
+ Thread.sleep(100)
+
+ assertThat(client.isClosed).isTrue
+ assertThat(client.closeString).isEqualTo(Some("Invalid status code received: 401 Status line: HTTP/1.1 401 Unauthorized"))
+ }
+
+ @Test
+ def noTypeFiledShouldTriggerError(server: GuiceJamesServer): Unit = {
+ println("started")
+ val port = server.getProbe(classOf[JmapGuiceProbe])
+ .getJmapPort
+ .getValue
+ val client = new ExampleClient(new URI(s"ws://127.0.0.1:$port/jmap/ws"))
+ client.addHeader("Authorization", "Basic Ym9iQGRvbWFpbi50bGQ6Ym9icGFzc3dvcmQ=")
+ client.addHeader("Accept", ACCEPT_RFC8621_VERSION_HEADER)
+
+ client.connectBlocking()
+
+ Thread.sleep(500)
+
+ client.send("""{
+ | "requestId": "req-36",
+ | "using": [ "urn:ietf:params:jmap:core"],
+ | "methodCalls": [
+ | [
+ | "Core/echo",
+ | {
+ | "arg1": "arg1data",
+ | "arg2": "arg2data"
+ | },
+ | "c1"
+ | ]
+ | ]
+ |}""".stripMargin)
+
+ Thread.sleep(500)
+
+
+ assertThat(client.receivedResponses).hasSize(1)
+ assertThatJson(client.receivedResponses.get(0)).isEqualTo(
+ """
+ |{
+ | "status":400,
+ | "detail":"The request was successfully parsed as JSON but did not match the type signature of the Request object: List((,List(JsonValidationError(List(Missing @type filed on a webSocket inbound message),ArraySeq()))))",
+ | "type":"urn:ietf:params:jmap:error:notRequest",
+ | "requestId":null,
+ | "@type":"RequestError"
+ |}
+ |""".stripMargin
+ )
}
@Test
- def badTypeFieldShouldTriggerError(): Unit = {
+ def badTypeFieldShouldTriggerError(server: GuiceJamesServer): Unit = {
/*
* TODO send something with @type being a JsNumber and get an error level error
* */
}
@Test
- def unknownTypeFieldShouldTriggerError(): Unit = {
+ def unknownTypeFieldShouldTriggerError(server: GuiceJamesServer): Unit = {
/*
* TODO send something with @type being a JsString("unknown") and get an error level error
* */
}
@Test
- def requestLevelErrorShouldReturnAPIError(): Unit = {
+ def requestLevelErrorShouldReturnAPIError(server: GuiceJamesServer): Unit = {
/*
* TODO send a request triggering a method level error (eg Mailbox/get with an invalid JSON payload)
* */
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org