openvidu-server: migrate PowerMockito to Mockito

v2
pabloFuente 2025-11-04 16:46:19 +01:00
parent 20630c0553
commit f120e6a70c
4 changed files with 59 additions and 57 deletions

View File

@ -312,12 +312,6 @@
<version>${version.openvidu.test.browsers}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${version.powermock}</version>
<scope>test</scope>
</dependency>
<!-- Test dependencies -->

View File

@ -27,8 +27,6 @@ import org.junit.jupiter.api.Assertions;
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;
@ -37,6 +35,7 @@ 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 org.springframework.test.util.ReflectionTestUtils;
import com.google.gson.JsonObject;
@ -91,7 +90,7 @@ public class WebhookIntegrationTest {
.filter(logger -> logger instanceof CDRLoggerWebhook).findFirst().get();
try {
this.webhook = Whitebox.getInternalState(cdrLoggerWebhook, "webhookSender");
this.webhook = (HttpWebhookSender) ReflectionTestUtils.getField(cdrLoggerWebhook, "webhookSender");
} catch (Exception e) {
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 {
HttpClient httpClient = null;
try {
httpClient = Whitebox.getInternalState(webhook, "httpClient");
httpClient = (HttpClient) ReflectionTestUtils.getField(webhook, "httpClient");
} catch (Exception e) {
Assertions.fail("Error getting private property from stubbed object: " + e.getMessage());
}
httpClient = PowerMockito.spy(httpClient);
doAnswer(invocationOnMock -> {
Thread.sleep(millisecondsDelayOnResponse);
return invocationOnMock.callRealMethod();
}).when(httpClient).execute(Mockito.any(HttpUriRequest.class));
Whitebox.setInternalState(webhook, "httpClient", 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 -> {
Thread.sleep(millisecondsDelayOnResponse);
return invocationOnMock.callRealMethod();
}).when(httpClient).execute(Mockito.any(HttpUriRequest.class));
} 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
@ -140,16 +163,18 @@ public class WebhookIntegrationTest {
final String sessionId = "WEBHOOK_TEST_SESSION";
// Test basic webhook functionality with delay
this.sessionRestController.initializeSession(Map.of("customSessionId", sessionId));
// Webhook event "sessionCreated" is delayed 500 ms
// Expected TimeoutException
// Expected TimeoutException when waiting only 250ms
assertThrows(TimeoutException.class, () -> {
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);
// Test RPC vs Webhook independence
this.sessionRestController.initializeConnection(sessionId, Map.of());
Session session = kurentoSessionManager.getSessionWithNotActive(sessionId);
@ -160,59 +185,43 @@ public class WebhookIntegrationTest {
kurentoSessionManager.joinRoom(participant, sessionId, 1);
// Webhook event "participantJoined" is delayed 500 ms
// Expected TimeoutException
// Expected TimeoutException when waiting only 250ms
assertThrows(TimeoutException.class, () -> {
CustomWebhook.waitForEvent("participantJoined", 250, TimeUnit.MILLISECONDS);
});
// 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(),
anySet(), anyInt(), refEq(null));
// Now webhook response for event "participantJoined" should be received
CustomWebhook.waitForEvent("participantJoined", 1000, TimeUnit.MILLISECONDS);
// Test multiple setHttpClientDelay calls work correctly
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"));
this.sessionRestController.signal(Map.of("session", sessionId, "type", "test1"));
CustomWebhook.waitForEvent("signalSent", 100, TimeUnit.MILLISECONDS);
// 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());
setHttpClientDelay(300);
this.sessionRestController.signal(Map.of("session", sessionId, "type", "test2"));
// 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");
// Receive all 5 webhook events with generous timeout
// 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);
// RPC signal notifications should have been sent immediately regardless of webhook delays
verify(rpcNotificationService, times(2)).sendNotification(refEq(participantPrivateId),
refEq(ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD), any());
// 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());
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", 1000, TimeUnit.MILLISECONDS);
});
setHttpClientDelay(1); // Reset to fast for cleanup
CustomWebhook.waitForEvent("participantLeft", 500, TimeUnit.MILLISECONDS);
} finally {
CustomWebhook.shutDown();

View File

@ -17,8 +17,8 @@ import org.kurento.client.ServerInfo;
import org.kurento.client.ServerManager;
import org.kurento.client.ServerType;
import org.mockito.Mockito;
import org.powermock.reflect.Whitebox;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.context.annotation.Bean;
import io.openvidu.server.kurento.core.KurentoSessionManager;
@ -52,7 +52,7 @@ public class IntegrationTestConfiguration {
LoadManager loadManager = null;
try {
loadManager = Whitebox.getInternalState(spy, "loadManager");
loadManager = (LoadManager) ReflectionTestUtils.getField(spy, "loadManager");
} catch (Exception e) {
Assertions.fail("Error getting private property from stubbed object: " + e.getMessage());
}

View File

@ -53,7 +53,6 @@
<version.junit>5.11.4</version.junit>
<version.selenium>4.26.0</version.selenium>
<version.mockito.core>5.14.2</version.mockito.core>
<version.powermock>2.0.9</version.powermock>
<version.janino>3.1.12</version.janino>
<version.dockerjava>3.6.0</version.dockerjava>
<!-- When upgrading also upgrade in openvidu-java-client pom.xml -->