forked from OpenVidu/openvidu
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
22c7e08
commit e60ac50
Showing
3 changed files
with
245 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
219 changes: 219 additions & 0 deletions
219
...vidu-server/src/test/java/io/openvidu/server/test/integration/WebhookIntegrationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
package io.openvidu.server.test.integration; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.anyInt; | ||
import static org.mockito.ArgumentMatchers.anySet; | ||
import static org.mockito.ArgumentMatchers.anyString; | ||
import static org.mockito.ArgumentMatchers.refEq; | ||
import static org.mockito.Mockito.doAnswer; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.times; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
import java.io.IOException; | ||
import java.util.Map; | ||
import java.util.concurrent.CountDownLatch; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.TimeoutException; | ||
|
||
import org.apache.http.StatusLine; | ||
import org.apache.http.client.ClientProtocolException; | ||
import org.apache.http.client.HttpClient; | ||
import org.apache.http.client.methods.CloseableHttpResponse; | ||
import org.apache.http.client.methods.HttpUriRequest; | ||
import org.junit.Assert; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.Mockito; | ||
import org.powermock.api.mockito.PowerMockito; | ||
import org.powermock.reflect.Whitebox; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.boot.test.mock.mockito.SpyBean; | ||
import org.springframework.test.context.ContextConfiguration; | ||
import org.springframework.test.context.TestPropertySource; | ||
import org.springframework.test.context.web.WebAppConfiguration; | ||
|
||
import com.google.gson.JsonObject; | ||
|
||
import io.openvidu.client.internal.ProtocolElements; | ||
import io.openvidu.java.client.ConnectionProperties; | ||
import io.openvidu.server.cdr.CallDetailRecord; | ||
import io.openvidu.server.core.Participant; | ||
import io.openvidu.server.core.Session; | ||
import io.openvidu.server.core.SessionEventsHandler; | ||
import io.openvidu.server.core.Token; | ||
import io.openvidu.server.kurento.core.KurentoSessionManager; | ||
import io.openvidu.server.rest.SessionRestController; | ||
import io.openvidu.server.rpc.RpcNotificationService; | ||
import io.openvidu.server.test.integration.config.IntegrationTestConfiguration; | ||
import io.openvidu.server.utils.GeoLocation; | ||
import io.openvidu.server.webhook.CDRLoggerWebhook; | ||
import io.openvidu.server.webhook.HttpWebhookSender; | ||
import io.openvidu.test.browsers.utils.webhook.CustomWebhook; | ||
|
||
/** | ||
* @author Pablo Fuente ([email protected]) | ||
*/ | ||
@SpringBootTest(properties = { "OPENVIDU_WEBHOOK=true", "OPENVIDU_WEBHOOK_ENDPOINT=http://localhost:7777/webhook", | ||
"OPENVIDU_WEBHOOK_HEADERS=[]", | ||
"OPENVIDU_WEBHOOK_EVENTS=[\"sessionCreated\",\"participantJoined\",\"participantLeft\",\"signalSent\"]" }) | ||
@TestPropertySource(locations = "classpath:integration-test.properties") | ||
@ContextConfiguration(classes = { IntegrationTestConfiguration.class }) | ||
@WebAppConfiguration | ||
public class WebhookIntegrationTest { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(WebhookIntegrationTest.class); | ||
|
||
@SpyBean | ||
private CallDetailRecord cdr; | ||
|
||
@SpyBean | ||
private SessionEventsHandler sessionEventsHandler; | ||
|
||
@SpyBean | ||
protected RpcNotificationService rpcNotificationService; | ||
|
||
@Autowired | ||
protected SessionRestController sessionRestController; | ||
|
||
@Autowired | ||
protected KurentoSessionManager kurentoSessionManager; | ||
|
||
private HttpWebhookSender webhook; | ||
|
||
private void mockWebhookHttpClient(int millisecondsDelayOnResponse) throws ClientProtocolException, IOException { | ||
CDRLoggerWebhook cdrLoggerWebhook = (CDRLoggerWebhook) cdr.getLoggers().stream() | ||
.filter(logger -> logger instanceof CDRLoggerWebhook).findFirst().get(); | ||
this.webhook = Whitebox.getInternalState(cdrLoggerWebhook, "webhookSender"); | ||
|
||
CloseableHttpResponse httpResponse = mock(CloseableHttpResponse.class); | ||
StatusLine statusLine = mock(StatusLine.class); | ||
when(statusLine.getStatusCode()).thenReturn(200); | ||
when(httpResponse.getStatusLine()).thenReturn(statusLine); | ||
|
||
this.setHttpClientDelay(millisecondsDelayOnResponse); | ||
} | ||
|
||
private void setHttpClientDelay(int millisecondsDelayOnResponse) throws ClientProtocolException, IOException { | ||
HttpClient httpClient = PowerMockito.spy((HttpClient) Whitebox.getInternalState(webhook, "httpClient")); | ||
doAnswer(invocationOnMock -> { | ||
Thread.sleep(millisecondsDelayOnResponse); | ||
return invocationOnMock.callRealMethod(); | ||
}).when(httpClient).execute(Mockito.any(HttpUriRequest.class)); | ||
Whitebox.setInternalState(webhook, "httpClient", httpClient); | ||
} | ||
|
||
@Test | ||
@DisplayName("Webhook event and RPC event should not interfere with each other") | ||
void webhookEventAndRpcEventShouldNotInterfereWithEachOtherTest() throws Exception { | ||
|
||
log.info("Webhook event and RPC event should not interfere with each other"); | ||
|
||
this.mockWebhookHttpClient(500); | ||
|
||
CountDownLatch initLatch = new CountDownLatch(1); | ||
CustomWebhook.main(new String[0], initLatch); | ||
|
||
try { | ||
|
||
if (!initLatch.await(30, TimeUnit.SECONDS)) { | ||
Assert.fail("Timeout waiting for webhook springboot app to start"); | ||
CustomWebhook.shutDown(); | ||
return; | ||
} | ||
|
||
final String sessionId = "WEBHOOK_TEST_SESSION"; | ||
|
||
this.sessionRestController.initializeSession(Map.of("customSessionId", sessionId)); | ||
|
||
// Webhook event "sessionCreated" is delayed 500 ms | ||
// Expected TimeoutException | ||
assertThrows(TimeoutException.class, () -> { | ||
CustomWebhook.waitForEvent("sessionCreated", 250, TimeUnit.MILLISECONDS); | ||
}); | ||
// Now webhook response for event "sessionCreated" should be received | ||
CustomWebhook.waitForEvent("sessionCreated", 750, TimeUnit.MILLISECONDS); | ||
|
||
this.sessionRestController.initializeConnection(sessionId, Map.of()); | ||
|
||
Session session = kurentoSessionManager.getSessionWithNotActive(sessionId); | ||
Token token = new Token("token", sessionId, new ConnectionProperties.Builder().build(), null); | ||
String participantPrivateId = "participantPrivateId"; | ||
Participant participant = kurentoSessionManager.newParticipant(session, participantPrivateId, token, null, | ||
mock(GeoLocation.class), "platform", "finalUserId"); | ||
kurentoSessionManager.joinRoom(participant, sessionId, 1); | ||
|
||
// Webhook event "participantJoined" is delayed 500 ms | ||
// Expected TimeoutException | ||
assertThrows(TimeoutException.class, () -> { | ||
CustomWebhook.waitForEvent("participantJoined", 250, TimeUnit.MILLISECONDS); | ||
}); | ||
|
||
// Client should have already received "connectionCreated" RPC response | ||
// nonetheless | ||
verify(sessionEventsHandler, times(1)).onParticipantJoined(refEq(participant), anyString(), anySet(), | ||
anyInt(), refEq(null)); | ||
|
||
// Now webhook response for event "participantJoined" should be received | ||
CustomWebhook.waitForEvent("participantJoined", 750, TimeUnit.MILLISECONDS); | ||
|
||
setHttpClientDelay(1); | ||
// These events will be received immediately | ||
this.sessionRestController.signal(Map.of("session", sessionId, "type", "1")); | ||
this.sessionRestController.signal(Map.of("session", sessionId, "type", "2")); | ||
setHttpClientDelay(500); | ||
// This event will be received after a delay | ||
this.sessionRestController.signal(Map.of("session", sessionId, "type", "3")); | ||
setHttpClientDelay(1); | ||
// These events should be received immediately after the delayed one | ||
this.sessionRestController.signal(Map.of("session", sessionId, "type", "4")); | ||
this.sessionRestController.signal(Map.of("session", sessionId, "type", "5")); | ||
|
||
// RPC signal notification should have already been sent 5 times, | ||
// no matter WebHook delays | ||
verify(rpcNotificationService, times(5)).sendNotification(refEq(participantPrivateId), | ||
refEq(ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD), any()); | ||
|
||
// Events received immediately | ||
JsonObject signal1 = CustomWebhook.waitForEvent("signalSent", 25, TimeUnit.MILLISECONDS); | ||
JsonObject signal2 = CustomWebhook.waitForEvent("signalSent", 25, TimeUnit.MILLISECONDS); | ||
// Events not received due to timeout | ||
assertThrows(TimeoutException.class, () -> { | ||
CustomWebhook.waitForEvent("signalSent", 25, TimeUnit.MILLISECONDS); | ||
}); | ||
assertThrows(TimeoutException.class, () -> { | ||
CustomWebhook.waitForEvent("signalSent", 25, TimeUnit.MILLISECONDS); | ||
}); | ||
// Events now received after timeout | ||
JsonObject signal3 = CustomWebhook.waitForEvent("signalSent", 500, TimeUnit.MILLISECONDS); | ||
JsonObject signal4 = CustomWebhook.waitForEvent("signalSent", 25, TimeUnit.MILLISECONDS); | ||
JsonObject signal5 = CustomWebhook.waitForEvent("signalSent", 25, TimeUnit.MILLISECONDS); | ||
|
||
// Order of webhook events should be honored | ||
Assert.assertEquals("Wrong signal type", "1", signal1.get("type").getAsString()); | ||
Assert.assertEquals("Wrong signal type", "2", signal2.get("type").getAsString()); | ||
Assert.assertEquals("Wrong signal type", "3", signal3.get("type").getAsString()); | ||
Assert.assertEquals("Wrong signal type", "4", signal4.get("type").getAsString()); | ||
Assert.assertEquals("Wrong signal type", "5", signal5.get("type").getAsString()); | ||
|
||
this.sessionRestController.closeConnection(sessionId, participant.getParticipantPublicId()); | ||
|
||
// Webhook is configured to receive "participantLeft" event | ||
CustomWebhook.waitForEvent("participantLeft", 25, TimeUnit.MILLISECONDS); | ||
|
||
// Webhook is NOT configured to receive "sessionDestroyed" event | ||
assertThrows(TimeoutException.class, () -> { | ||
CustomWebhook.waitForEvent("sessionDestroyed", 500, TimeUnit.MILLISECONDS); | ||
}); | ||
|
||
} finally { | ||
CustomWebhook.shutDown(); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters