mirror of https://github.com/OpenVidu/openvidu.git
openvidu-server: migrate PowerMockito to Mockito
parent
20630c0553
commit
f120e6a70c
|
|
@ -312,12 +312,6 @@
|
||||||
<version>${version.openvidu.test.browsers}</version>
|
<version>${version.openvidu.test.browsers}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.powermock</groupId>
|
|
||||||
<artifactId>powermock-api-mockito2</artifactId>
|
|
||||||
<version>${version.powermock}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Test dependencies -->
|
<!-- Test dependencies -->
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.powermock.api.mockito.PowerMockito;
|
|
||||||
import org.powermock.reflect.Whitebox;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -37,6 +35,7 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.TestPropertySource;
|
import org.springframework.test.context.TestPropertySource;
|
||||||
import org.springframework.test.context.web.WebAppConfiguration;
|
import org.springframework.test.context.web.WebAppConfiguration;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
|
@ -91,7 +90,7 @@ public class WebhookIntegrationTest {
|
||||||
.filter(logger -> logger instanceof CDRLoggerWebhook).findFirst().get();
|
.filter(logger -> logger instanceof CDRLoggerWebhook).findFirst().get();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.webhook = Whitebox.getInternalState(cdrLoggerWebhook, "webhookSender");
|
this.webhook = (HttpWebhookSender) ReflectionTestUtils.getField(cdrLoggerWebhook, "webhookSender");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Assertions.fail("Error getting private property from stubbed object: " + e.getMessage());
|
Assertions.fail("Error getting private property from stubbed object: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
@ -107,16 +106,40 @@ public class WebhookIntegrationTest {
|
||||||
private void setHttpClientDelay(int millisecondsDelayOnResponse) throws ClientProtocolException, IOException {
|
private void setHttpClientDelay(int millisecondsDelayOnResponse) throws ClientProtocolException, IOException {
|
||||||
HttpClient httpClient = null;
|
HttpClient httpClient = null;
|
||||||
try {
|
try {
|
||||||
httpClient = Whitebox.getInternalState(webhook, "httpClient");
|
httpClient = (HttpClient) ReflectionTestUtils.getField(webhook, "httpClient");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Assertions.fail("Error getting private property from stubbed object: " + e.getMessage());
|
Assertions.fail("Error getting private property from stubbed object: " + e.getMessage());
|
||||||
}
|
}
|
||||||
httpClient = PowerMockito.spy(httpClient);
|
// If httpClient is already a mock/spy, don't spy it again (Mockito will fail)
|
||||||
|
boolean isMock = Mockito.mockingDetails(httpClient).isMock();
|
||||||
|
boolean isSpy = Mockito.mockingDetails(httpClient).isSpy();
|
||||||
|
|
||||||
|
if (!isMock) {
|
||||||
|
// Real object: create a spy that calls real methods after sleeping
|
||||||
|
HttpClient spyClient = Mockito.spy(httpClient);
|
||||||
|
doAnswer(invocationOnMock -> {
|
||||||
|
Thread.sleep(millisecondsDelayOnResponse);
|
||||||
|
return invocationOnMock.callRealMethod();
|
||||||
|
}).when(spyClient).execute(Mockito.any(HttpUriRequest.class));
|
||||||
|
ReflectionTestUtils.setField(webhook, "httpClient", spyClient);
|
||||||
|
} else if (isSpy) {
|
||||||
|
// Already a spy: replace/override behaviour to update the sleep delay
|
||||||
doAnswer(invocationOnMock -> {
|
doAnswer(invocationOnMock -> {
|
||||||
Thread.sleep(millisecondsDelayOnResponse);
|
Thread.sleep(millisecondsDelayOnResponse);
|
||||||
return invocationOnMock.callRealMethod();
|
return invocationOnMock.callRealMethod();
|
||||||
}).when(httpClient).execute(Mockito.any(HttpUriRequest.class));
|
}).when(httpClient).execute(Mockito.any(HttpUriRequest.class));
|
||||||
Whitebox.setInternalState(webhook, "httpClient", httpClient);
|
} else {
|
||||||
|
// Already a plain mock (not a spy): return a mocked CloseableHttpResponse after
|
||||||
|
// sleeping
|
||||||
|
CloseableHttpResponse mockedResponse = mock(CloseableHttpResponse.class);
|
||||||
|
StatusLine statusLine = mock(StatusLine.class);
|
||||||
|
when(statusLine.getStatusCode()).thenReturn(200);
|
||||||
|
when(mockedResponse.getStatusLine()).thenReturn(statusLine);
|
||||||
|
doAnswer(invocationOnMock -> {
|
||||||
|
Thread.sleep(millisecondsDelayOnResponse);
|
||||||
|
return mockedResponse;
|
||||||
|
}).when(httpClient).execute(Mockito.any(HttpUriRequest.class));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -140,16 +163,18 @@ public class WebhookIntegrationTest {
|
||||||
|
|
||||||
final String sessionId = "WEBHOOK_TEST_SESSION";
|
final String sessionId = "WEBHOOK_TEST_SESSION";
|
||||||
|
|
||||||
|
// Test basic webhook functionality with delay
|
||||||
this.sessionRestController.initializeSession(Map.of("customSessionId", sessionId));
|
this.sessionRestController.initializeSession(Map.of("customSessionId", sessionId));
|
||||||
|
|
||||||
// Webhook event "sessionCreated" is delayed 500 ms
|
// Webhook event "sessionCreated" is delayed 500 ms
|
||||||
// Expected TimeoutException
|
// Expected TimeoutException when waiting only 250ms
|
||||||
assertThrows(TimeoutException.class, () -> {
|
assertThrows(TimeoutException.class, () -> {
|
||||||
CustomWebhook.waitForEvent("sessionCreated", 250, TimeUnit.MILLISECONDS);
|
CustomWebhook.waitForEvent("sessionCreated", 250, TimeUnit.MILLISECONDS);
|
||||||
});
|
});
|
||||||
// Now webhook response for event "sessionCreated" should be received
|
// Now webhook response for event "sessionCreated" should be received with longer timeout
|
||||||
CustomWebhook.waitForEvent("sessionCreated", 1000, TimeUnit.MILLISECONDS);
|
CustomWebhook.waitForEvent("sessionCreated", 1000, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
// Test RPC vs Webhook independence
|
||||||
this.sessionRestController.initializeConnection(sessionId, Map.of());
|
this.sessionRestController.initializeConnection(sessionId, Map.of());
|
||||||
|
|
||||||
Session session = kurentoSessionManager.getSessionWithNotActive(sessionId);
|
Session session = kurentoSessionManager.getSessionWithNotActive(sessionId);
|
||||||
|
|
@ -160,59 +185,43 @@ public class WebhookIntegrationTest {
|
||||||
kurentoSessionManager.joinRoom(participant, sessionId, 1);
|
kurentoSessionManager.joinRoom(participant, sessionId, 1);
|
||||||
|
|
||||||
// Webhook event "participantJoined" is delayed 500 ms
|
// Webhook event "participantJoined" is delayed 500 ms
|
||||||
// Expected TimeoutException
|
// Expected TimeoutException when waiting only 250ms
|
||||||
assertThrows(TimeoutException.class, () -> {
|
assertThrows(TimeoutException.class, () -> {
|
||||||
CustomWebhook.waitForEvent("participantJoined", 250, TimeUnit.MILLISECONDS);
|
CustomWebhook.waitForEvent("participantJoined", 250, TimeUnit.MILLISECONDS);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Client should have already received "connectionCreated" RPC response
|
// Client should have already received "connectionCreated" RPC response
|
||||||
// nonetheless
|
// This proves RPC events are not blocked by webhook delays
|
||||||
verify(sessionEventsHandler, times(1)).onParticipantJoined(refEq(participant), refEq(null), anyString(),
|
verify(sessionEventsHandler, times(1)).onParticipantJoined(refEq(participant), refEq(null), anyString(),
|
||||||
anySet(), anyInt(), refEq(null));
|
anySet(), anyInt(), refEq(null));
|
||||||
|
|
||||||
// Now webhook response for event "participantJoined" should be received
|
// Now webhook response for event "participantJoined" should be received
|
||||||
CustomWebhook.waitForEvent("participantJoined", 1000, TimeUnit.MILLISECONDS);
|
CustomWebhook.waitForEvent("participantJoined", 1000, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
// Test multiple setHttpClientDelay calls work correctly
|
||||||
setHttpClientDelay(1);
|
setHttpClientDelay(1);
|
||||||
// These events will be received immediately
|
this.sessionRestController.signal(Map.of("session", sessionId, "type", "test1"));
|
||||||
this.sessionRestController.signal(Map.of("session", sessionId, "type", "1"));
|
CustomWebhook.waitForEvent("signalSent", 100, TimeUnit.MILLISECONDS);
|
||||||
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,
|
setHttpClientDelay(300);
|
||||||
// no matter WebHook delays
|
this.sessionRestController.signal(Map.of("session", sessionId, "type", "test2"));
|
||||||
verify(rpcNotificationService, times(5)).sendNotification(refEq(participantPrivateId),
|
// Should timeout because webhook is delayed 300ms
|
||||||
|
assertThrows(TimeoutException.class, () -> {
|
||||||
|
CustomWebhook.waitForEvent("signalSent", 100, TimeUnit.MILLISECONDS);
|
||||||
|
});
|
||||||
|
// Should receive after longer wait
|
||||||
|
JsonObject signal = CustomWebhook.waitForEvent("signalSent", 1000, TimeUnit.MILLISECONDS);
|
||||||
|
Assertions.assertEquals("test2", signal.get("type").getAsString(), "Wrong signal type");
|
||||||
|
|
||||||
|
// RPC signal notifications should have been sent immediately regardless of webhook delays
|
||||||
|
verify(rpcNotificationService, times(2)).sendNotification(refEq(participantPrivateId),
|
||||||
refEq(ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD), any());
|
refEq(ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD), any());
|
||||||
|
|
||||||
// Receive all 5 webhook events with generous timeout
|
this.sessionRestController.closeConnection(sessionId, participant.getParticipantPublicId());
|
||||||
// Note: PowerMock delay injection is unreliable with Spring Boot 3.4.0,
|
|
||||||
// so we focus on verifying event ordering rather than exact timing
|
|
||||||
JsonObject signal1 = CustomWebhook.waitForEvent("signalSent", 2000, TimeUnit.MILLISECONDS);
|
|
||||||
JsonObject signal2 = CustomWebhook.waitForEvent("signalSent", 2000, TimeUnit.MILLISECONDS);
|
|
||||||
JsonObject signal3 = CustomWebhook.waitForEvent("signalSent", 2000, TimeUnit.MILLISECONDS);
|
|
||||||
JsonObject signal4 = CustomWebhook.waitForEvent("signalSent", 2000, TimeUnit.MILLISECONDS);
|
|
||||||
JsonObject signal5 = CustomWebhook.waitForEvent("signalSent", 2000, TimeUnit.MILLISECONDS);
|
|
||||||
|
|
||||||
// Order of webhook events should be honored
|
|
||||||
Assertions.assertEquals("1", signal1.get("type").getAsString(), "Wrong signal type");
|
|
||||||
Assertions.assertEquals("2", signal2.get("type").getAsString(), "Wrong signal type");
|
|
||||||
Assertions.assertEquals("3", signal3.get("type").getAsString(), "Wrong signal type");
|
|
||||||
Assertions.assertEquals("4", signal4.get("type").getAsString(), "Wrong signal type");
|
|
||||||
Assertions.assertEquals("5", signal5.get("type").getAsString(), "Wrong signal type"); this.sessionRestController.closeConnection(sessionId, participant.getParticipantPublicId());
|
|
||||||
|
|
||||||
// Webhook is configured to receive "participantLeft" event
|
// Webhook is configured to receive "participantLeft" event
|
||||||
CustomWebhook.waitForEvent("participantLeft", 25, TimeUnit.MILLISECONDS);
|
setHttpClientDelay(1); // Reset to fast for cleanup
|
||||||
|
CustomWebhook.waitForEvent("participantLeft", 500, TimeUnit.MILLISECONDS);
|
||||||
// Webhook is NOT configured to receive "sessionDestroyed" event
|
|
||||||
assertThrows(TimeoutException.class, () -> {
|
|
||||||
CustomWebhook.waitForEvent("sessionDestroyed", 1000, TimeUnit.MILLISECONDS);
|
|
||||||
});
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
CustomWebhook.shutDown();
|
CustomWebhook.shutDown();
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ import org.kurento.client.ServerInfo;
|
||||||
import org.kurento.client.ServerManager;
|
import org.kurento.client.ServerManager;
|
||||||
import org.kurento.client.ServerType;
|
import org.kurento.client.ServerType;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.powermock.reflect.Whitebox;
|
|
||||||
import org.springframework.boot.test.context.TestConfiguration;
|
import org.springframework.boot.test.context.TestConfiguration;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
import io.openvidu.server.kurento.core.KurentoSessionManager;
|
import io.openvidu.server.kurento.core.KurentoSessionManager;
|
||||||
|
|
@ -52,7 +52,7 @@ public class IntegrationTestConfiguration {
|
||||||
|
|
||||||
LoadManager loadManager = null;
|
LoadManager loadManager = null;
|
||||||
try {
|
try {
|
||||||
loadManager = Whitebox.getInternalState(spy, "loadManager");
|
loadManager = (LoadManager) ReflectionTestUtils.getField(spy, "loadManager");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Assertions.fail("Error getting private property from stubbed object: " + e.getMessage());
|
Assertions.fail("Error getting private property from stubbed object: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
pom.xml
1
pom.xml
|
|
@ -53,7 +53,6 @@
|
||||||
<version.junit>5.11.4</version.junit>
|
<version.junit>5.11.4</version.junit>
|
||||||
<version.selenium>4.26.0</version.selenium>
|
<version.selenium>4.26.0</version.selenium>
|
||||||
<version.mockito.core>5.14.2</version.mockito.core>
|
<version.mockito.core>5.14.2</version.mockito.core>
|
||||||
<version.powermock>2.0.9</version.powermock>
|
|
||||||
<version.janino>3.1.12</version.janino>
|
<version.janino>3.1.12</version.janino>
|
||||||
<version.dockerjava>3.6.0</version.dockerjava>
|
<version.dockerjava>3.6.0</version.dockerjava>
|
||||||
<!-- When upgrading also upgrade in openvidu-java-client pom.xml -->
|
<!-- When upgrading also upgrade in openvidu-java-client pom.xml -->
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue