diff --git a/openvidu-test-e2e/pom.xml b/openvidu-test-e2e/pom.xml
index 1b62c958..a33ecb69 100644
--- a/openvidu-test-e2e/pom.xml
+++ b/openvidu-test-e2e/pom.xml
@@ -163,6 +163,12 @@
openvidu-java-client
2.8.1
+
+
+ com.mashape.unirest
+ unirest-java
+ 1.4.9
+
diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java
index 5e6c8747..b75e559c 100644
--- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java
+++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java
@@ -44,6 +44,7 @@ import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import org.apache.commons.io.FileUtils;
+import org.apache.http.HttpStatus;
import org.jcodec.api.FrameGrab;
import org.jcodec.api.JCodecException;
import org.jcodec.common.model.Picture;
@@ -75,19 +76,29 @@ import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.stream.JsonReader;
+import com.mashape.unirest.http.HttpMethod;
import io.github.bonigarcia.SeleniumExtension;
import io.github.bonigarcia.wdm.WebDriverManager;
+import io.openvidu.java.client.KurentoOptions;
+import io.openvidu.java.client.MediaMode;
import io.openvidu.java.client.OpenVidu;
import io.openvidu.java.client.OpenViduHttpException;
import io.openvidu.java.client.OpenViduJavaClientException;
+import io.openvidu.java.client.OpenViduRole;
import io.openvidu.java.client.Recording;
+import io.openvidu.java.client.Recording.OutputMode;
+import io.openvidu.java.client.RecordingMode;
+import io.openvidu.java.client.Session;
+import io.openvidu.java.client.SessionProperties;
+import io.openvidu.java.client.TokenOptions;
import io.openvidu.test.e2e.browser.BrowserUser;
import io.openvidu.test.e2e.browser.ChromeAndroidUser;
import io.openvidu.test.e2e.browser.ChromeUser;
import io.openvidu.test.e2e.browser.FirefoxUser;
import io.openvidu.test.e2e.browser.OperaUser;
import io.openvidu.test.e2e.utils.CommandLineExecuter;
+import io.openvidu.test.e2e.utils.CustomHttpClient;
import io.openvidu.test.e2e.utils.MultimediaFileMetadata;
import io.openvidu.test.e2e.utils.Unzipper;
@@ -206,7 +217,9 @@ public class OpenViduTestAppE2eTest {
@AfterEach
void dispose() {
- user.dispose();
+ if (user != null) {
+ user.dispose();
+ }
Iterator it = otherUsers.iterator();
while (it.hasNext()) {
BrowserUser other = it.next();
@@ -1898,6 +1911,153 @@ public class OpenViduTestAppE2eTest {
gracefullyLeaveParticipants(2);
}
+ @Test
+ @DisplayName("openvidu-java-client test")
+ void openViduJavaClientTest() throws Exception {
+
+ setupBrowser("chrome");
+
+ log.info("openvidu-java-client test");
+
+ user.getDriver().findElement(By.id("one2one-btn")).click();
+
+ final String customSessionId = "openviduJavaClientSession";
+ final String serverData1 = "SERVER_DATA_1";
+ final String serverData2 = "SERVER_DATA_2";
+
+ Assert.assertFalse("OV.fetch() should return false if OV.createSession() has not been called", OV.fetch());
+ List sessions = OV.getActiveSessions();
+ Assert.assertEquals("Expected no active sessions but found " + sessions.size(), sessions.size(), 0);
+
+ SessionProperties properties = new SessionProperties.Builder().customSessionId(customSessionId)
+ .mediaMode(MediaMode.ROUTED).recordingMode(RecordingMode.ALWAYS)
+ .defaultOutputMode(OutputMode.INDIVIDUAL).build();
+ Session session = OV.createSession(properties);
+
+ Assert.assertFalse("Session.fetch() should return false after OpenVidu.createSession()", session.fetch());
+ Assert.assertFalse("OpenVidu.fetch() should return false after OpenVidu.createSession()", OV.fetch());
+ sessions = OV.getActiveSessions();
+ Assert.assertEquals("Expected 1 active session but found " + sessions.size(), 1, sessions.size());
+
+ KurentoOptions kurentoOptions = new KurentoOptions.Builder().videoMaxRecvBandwidth(250)
+ .allowedFilters(new String[] { "GStreamerFilter" }).build();
+ TokenOptions tokenOptions1 = new TokenOptions.Builder().role(OpenViduRole.MODERATOR).data(serverData1)
+ .kurentoOptions(kurentoOptions).build();
+ String token1 = session.generateToken(tokenOptions1);
+
+ TokenOptions tokenOptions2 = new TokenOptions.Builder().role(OpenViduRole.SUBSCRIBER).data(serverData2).build();
+ String token2 = session.generateToken(tokenOptions2);
+
+ Assert.assertFalse("Session.fetch() should return false until a user has connected", session.fetch());
+
+ user.getDriver().findElement(By.id("session-settings-btn-0")).click();
+ Thread.sleep(1000);
+
+ // Set token 1
+ WebElement tokeInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
+ tokeInput.clear();
+ tokeInput.sendKeys(token1);
+
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ user.getDriver().findElement(By.id("session-settings-btn-1")).click();
+ Thread.sleep(1000);
+
+ // Set token 2
+ tokeInput = user.getDriver().findElement(By.cssSelector("#custom-token-div input"));
+ tokeInput.clear();
+ tokeInput.sendKeys(token2);
+
+ user.getDriver().findElement(By.id("save-btn")).click();
+ Thread.sleep(1000);
+
+ user.getDriver().findElements(By.className("join-btn")).forEach(el -> el.sendKeys(Keys.ENTER));
+
+ user.getEventManager().waitUntilEventReaches("connectionCreated", 4);
+ user.getEventManager().waitUntilEventReaches("accessAllowed", 1);
+ user.getEventManager().waitUntilEventReaches("streamCreated", 2);
+ user.getEventManager().waitUntilEventReaches("streamPlaying", 2);
+
+ final int numberOfVideos = user.getDriver().findElements(By.tagName("video")).size();
+ Assert.assertEquals("Expected 2 videos but found " + numberOfVideos, numberOfVideos, 2);
+ Assert.assertTrue("Videos were expected to have audio and video tracks", user.getEventManager()
+ .assertMediaTracks(user.getDriver().findElements(By.tagName("video")), true, true));
+
+ Assert.assertTrue("Session.fetch() should return true after users connected", session.fetch());
+
+ // Verify that users have the role and data they were assigned through
+ // TokenOptions
+
+ gracefullyLeaveParticipants(2);
+ }
+
+ @Test
+ @DisplayName("REST API test")
+ void restApiTest() throws Exception {
+
+ CustomHttpClient restClient = new CustomHttpClient(OPENVIDU_URL, OPENVIDU_SECRET);
+
+ // 401
+ restClient.testAuthorizationError();
+
+ // GET /api/sessions before any session created
+ restClient.rest(HttpMethod.GET, "/api/sessions/NOT_EXISTS", HttpStatus.SC_NOT_FOUND);
+ Map returnValues = new HashMap<>();
+ returnValues.put("numberOfElements", new Integer(0));
+ returnValues.put("content", "[]");
+ restClient.rest(HttpMethod.GET, "/api/sessions", null, HttpStatus.SC_OK, true, returnValues);
+
+ /** POST /api/sessions **/
+ // 400
+ String body = "{'mediaMode': 'ROUTED', 'recordingMode': 'ALWAYS', 'customSessionId': 999}";
+ restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'mediaMode': 'ROUTED', 'recordingMode': false}";
+ restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'mediaMode': 'NOT_EXISTS'}";
+ restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'mediaMode': 'ROUTED', 'recordingMode': 'NOT_EXISTS'}";
+ restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'mediaMode': 'ROUTED', 'recordingMode': 'ALWAYS', 'defaultOutputMode': 'NOT_EXISTS'}";
+ restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'mediaMode': 'ROUTED', 'recordingMode': 'ALWAYS', 'defaultOutputMode': 'INDIVIDUAL', 'defaultRecordingLayout': 'NOT_EXISTS'}";
+ restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_BAD_REQUEST);
+
+ // 200
+ body = "{'mediaMode': 'ROUTED', 'recordingMode': 'ALWAYS', 'customSessionId': 'CUSTOM_SESSION_ID', 'defaultOutputMode': 'INDIVIDUAL', 'defaultRecordingLayout': 'BEST_FIT'}";
+ restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_OK, true,
+ "{'id': 'STR', 'createdAt': 0}");
+
+ // 409
+ body = "{'customSessionId': 'CUSTOM_SESSION_ID'}";
+ restClient.rest(HttpMethod.POST, "/api/sessions", body, HttpStatus.SC_CONFLICT);
+
+ // GET /api/sessions after session created
+ restClient.rest(HttpMethod.GET, "/api/sessions/CUSTOM_SESSION_ID", null, HttpStatus.SC_OK, true,
+ "{'sessionId':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','customSessionId':'STR','connections':{'numberOfElements':0,'content':[]},'recording':true}");
+ returnValues = new HashMap<>();
+ returnValues.put("numberOfElements", new Integer(1));
+ returnValues.put("content", new org.json.JSONArray());
+ restClient.rest(HttpMethod.GET, "/api/sessions", null, HttpStatus.SC_OK, true, returnValues);
+
+ /** POST /api/tokens **/
+ // 400
+ body = "{'session': true}";
+ restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'NOT_EXISTS', 'data': 'DATA'}";
+ restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'MODERATOR', 'data': 999}";
+ restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'MODERATOR', 'data': 'SERVER_DATA', 'kurentoOptions': false}";
+ restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+ body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'MODERATOR', 'data': 'SERVER_DATA', 'kurentoOptions': {'allowedFilters': 'NOT_EXISTS'}}";
+ restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_BAD_REQUEST);
+
+ // 200
+ body = "{'session': 'CUSTOM_SESSION_ID', 'role': 'MODERATOR', 'data': 'SERVER_DATA', 'kurentoOptions': {'allowedFilters': ['GStreamerFilter']}}";
+ restClient.rest(HttpMethod.POST, "/api/tokens", body, HttpStatus.SC_OK);
+ }
+
private void listEmptyRecordings() {
// List existing recordings (empty)
user.getDriver().findElement(By.id("list-recording-btn")).click();
diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/CustomHttpClient.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/CustomHttpClient.java
new file mode 100644
index 00000000..cbc31681
--- /dev/null
+++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/utils/CustomHttpClient.java
@@ -0,0 +1,217 @@
+/*
+ * (C) Copyright 2017-2019 OpenVidu (https://openvidu.io/)
+ *
+ * Licensed 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 io.openvidu.test.e2e.utils;
+
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.junit.Assert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.mashape.unirest.http.HttpMethod;
+import com.mashape.unirest.http.HttpResponse;
+import com.mashape.unirest.http.JsonNode;
+import com.mashape.unirest.http.Unirest;
+import com.mashape.unirest.http.exceptions.UnirestException;
+import com.mashape.unirest.request.HttpRequest;
+import com.mashape.unirest.request.HttpRequestWithBody;
+
+public class CustomHttpClient {
+
+ private static final Logger log = LoggerFactory.getLogger(CustomHttpClient.class);
+
+ private String openviduUrl;
+ private String headerAuth;
+
+ private JSONParser parser = new JSONParser();
+
+ public CustomHttpClient(String openviduUrl, String openviduSecret) {
+ this.openviduUrl = openviduUrl.replaceFirst("/*$", "");
+ this.headerAuth = "Basic " + Base64.getEncoder().encodeToString(("OPENVIDUAPP:" + openviduSecret).getBytes());
+
+ SSLContext sslContext = null;
+ try {
+ sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy() {
+ public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ return true;
+ }
+ }).build();
+ } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
+ Assert.fail("Error building custom HttpClient: " + e.getMessage());
+ }
+ HttpClient unsafeHttpClient = HttpClients.custom().setSSLContext(sslContext)
+ .setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
+ Unirest.setHttpClient(unsafeHttpClient);
+ }
+
+ public void testAuthorizationError() {
+ try {
+ String wrongCredentials = "Basic "
+ + Base64.getEncoder().encodeToString(("OPENVIDUAPP:WRONG_SECRET").getBytes());
+ Assert.assertEquals("Expected 401 status (unauthorized)", HttpStatus.SC_UNAUTHORIZED, Unirest
+ .get(openviduUrl + "/config").header("Authorization", wrongCredentials).asJson().getStatus());
+ } catch (UnirestException e) {
+ Assert.fail("Error testing UNAUTHORIZED rest method: " + e.getMessage());
+ }
+ }
+
+ public void rest(HttpMethod method, String path, int status) {
+ this.commonRest(method, path, null, status);
+ }
+
+ public void rest(HttpMethod method, String path, String body, int status) {
+ this.commonRest(method, path, body, status);
+ }
+
+ public void rest(HttpMethod method, String path, String body, int status, boolean exactFields,
+ String jsonElmentString) {
+ JSONObject json = this.commonRest(method, path, body, status);
+ JSONObject jsonObjExpected = null;
+ jsonElmentString.replaceAll("'", "\"");
+ try {
+ jsonObjExpected = new JSONObject((String) jsonElmentString);
+ } catch (JSONException e1) {
+ Assert.fail("Expected json element is a string without a JSON format: " + jsonElmentString);
+ }
+
+ if (exactFields) {
+ Assert.assertEquals("Error in number of keys in JSON response to POST " + path, jsonObjExpected.length(),
+ json.length());
+ }
+ for (String key : jsonObjExpected.keySet()) {
+ json.get(key);
+ }
+ }
+
+ public void rest(HttpMethod method, String path, String body, int status, boolean exactFields,
+ Map jsonResponse) {
+ org.json.JSONObject json = this.commonRest(method, path, body, status);
+
+ if (exactFields) {
+ Assert.assertEquals("Error in number of keys in JSON response to POST " + path, jsonResponse.size(),
+ json.length());
+ }
+
+ for (Entry entry : jsonResponse.entrySet()) {
+ Object value = entry.getValue();
+
+ if (value instanceof String) {
+ try {
+ JSONObject jsonObjExpected = new JSONObject((String) value);
+ JSONObject jsonObjActual = json.getJSONObject(entry.getKey());
+ // COMPARE
+
+ } catch (JSONException e1) {
+ try {
+ JSONArray jsonArrayExpected = new JSONArray((String) value);
+ JSONArray jsonArrayActual = json.getJSONArray(entry.getKey());
+ // COMPARE
+
+ } catch (JSONException e2) {
+ Assert.assertEquals("JSON field " + entry.getKey() + " has not expected value", (String) value,
+ json.getInt(entry.getKey()));
+ }
+ }
+ } else if (value instanceof Integer) {
+ Assert.assertEquals("JSON field " + entry.getKey() + " has not expected value", (int) value,
+ json.getInt(entry.getKey()));
+ } else if (value instanceof Long) {
+ Assert.assertEquals("JSON field " + entry.getKey() + " has not expected value", (long) value,
+ json.getLong(entry.getKey()));
+ } else if (value instanceof Double) {
+ Assert.assertEquals("JSON field " + entry.getKey() + " has not expected value", (double) value,
+ json.getDouble(entry.getKey()), 0.001);
+ } else if (value instanceof Boolean) {
+ Assert.assertEquals("JSON field " + entry.getKey() + " has not expected value", (boolean) value,
+ json.getBoolean(entry.getKey()));
+ } else if (value instanceof JSONArray) {
+ json.getJSONArray(entry.getKey());
+ } else if (value instanceof JSONObject) {
+ json.getJSONObject(entry.getKey());
+ } else {
+ Assert.fail("JSON response field cannot be parsed: " + entry.toString());
+ }
+ }
+ }
+
+ private org.json.JSONObject commonRest(HttpMethod method, String path, String body, int status) {
+ HttpResponse jsonResponse = null;
+ org.json.JSONObject json = null;
+ path = openviduUrl + (path.startsWith("/") ? path : ("/" + path));
+
+ HttpRequest request = null;
+ if (body != null && !body.isEmpty()) {
+ switch (method) {
+ case POST:
+ request = Unirest.post(path);
+ break;
+ case PUT:
+ request = Unirest.put(path);
+ break;
+ default:
+ break;
+ }
+ ((HttpRequestWithBody) request).header("Content-Type", "application/json").body(body.replaceAll("'", "\""));
+ } else {
+ switch (method) {
+ case GET:
+ request = Unirest.get(path);
+ break;
+ case POST:
+ request = Unirest.post(path);
+ break;
+ case DELETE:
+ request = Unirest.delete(path);
+ break;
+ default:
+ break;
+ }
+ request.header("Content-Type", "application/x-www-form-urlencoded");
+ }
+ try {
+ jsonResponse = request.header("Authorization", this.headerAuth).asJson();
+ if (jsonResponse.getBody() != null) {
+ json = jsonResponse.getBody().getObject();
+ }
+ } catch (UnirestException e) {
+ log.error(e.getMessage());
+ Assert.fail("Error sending request to " + path + ": " + e.getMessage());
+ }
+ Assert.assertEquals(path + " expected to return status " + status, status, jsonResponse.getStatus());
+ return json;
+ }
+}