From f120e6a70cb3d2192d2ae2753d4f84420578081a Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Tue, 4 Nov 2025 16:46:19 +0100 Subject: [PATCH] openvidu-server: migrate PowerMockito to Mockito --- openvidu-server/pom.xml | 6 - .../integration/WebhookIntegrationTest.java | 105 ++++++++++-------- .../config/IntegrationTestConfiguration.java | 4 +- pom.xml | 1 - 4 files changed, 59 insertions(+), 57 deletions(-) diff --git a/openvidu-server/pom.xml b/openvidu-server/pom.xml index 7cd1e37da..5933d639a 100644 --- a/openvidu-server/pom.xml +++ b/openvidu-server/pom.xml @@ -312,12 +312,6 @@ ${version.openvidu.test.browsers} test - - org.powermock - powermock-api-mockito2 - ${version.powermock} - test - diff --git a/openvidu-server/src/test/java/io/openvidu/server/test/integration/WebhookIntegrationTest.java b/openvidu-server/src/test/java/io/openvidu/server/test/integration/WebhookIntegrationTest.java index 5cfb5bc42..08ebcbc9c 100644 --- a/openvidu-server/src/test/java/io/openvidu/server/test/integration/WebhookIntegrationTest.java +++ b/openvidu-server/src/test/java/io/openvidu/server/test/integration/WebhookIntegrationTest.java @@ -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(); diff --git a/openvidu-server/src/test/java/io/openvidu/server/test/integration/config/IntegrationTestConfiguration.java b/openvidu-server/src/test/java/io/openvidu/server/test/integration/config/IntegrationTestConfiguration.java index 6e6b8f601..cc0f629d2 100644 --- a/openvidu-server/src/test/java/io/openvidu/server/test/integration/config/IntegrationTestConfiguration.java +++ b/openvidu-server/src/test/java/io/openvidu/server/test/integration/config/IntegrationTestConfiguration.java @@ -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()); } diff --git a/pom.xml b/pom.xml index 5101065c0..5aba58bba 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,6 @@ 5.11.4 4.26.0 5.14.2 - 2.0.9 3.1.12 3.6.0