diff --git a/core/pom.xml b/core/pom.xml index 9bff5e5ad0b2..06e645e44f0a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -491,6 +491,12 @@ THE SOFTWARE. test-annotations test + + org.jenkins-ci.main + jenkins-test-harness + 2353.ve3f890c6eea_f + test + org.junit.jupiter junit-jupiter diff --git a/core/src/test/java/hudson/cli/CLIActionTest.java b/core/src/test/java/hudson/cli/CLIActionTest.java new file mode 100644 index 000000000000..a8065dcbe954 --- /dev/null +++ b/core/src/test/java/hudson/cli/CLIActionTest.java @@ -0,0 +1,221 @@ +package hudson.cli; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Optional; +import javax.servlet.http.HttpServletResponse; +import jenkins.model.Jenkins; +import jenkins.websocket.WebSocketSession; +import jenkins.websocket.WebSockets; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.MockAuthorizationStrategy; +import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.StaplerRequest2; +import org.kohsuke.stapler.StaplerResponse2; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; + +public class CLIActionTest { + + @Rule + public JenkinsRule jenkinsRule = new JenkinsRule(); + + private final CLIAction cliAction = new CLIAction(); + + @Test + public void testDoCommand() throws ServletException, IOException { + Jenkins jenkins = jenkinsRule.getInstance(); + jenkins.setSecurityRealm(jenkinsRule.createDummySecurityRealm()); + jenkinsRule.jenkins.setAuthorizationStrategy( + new org.jvnet.hudson.test.MockAuthorizationStrategy() + .grant(Jenkins.READ) + .everywhere() + .to("user")); + + StaplerRequest2 req = mock(StaplerRequest2.class); + StaplerResponse2 rsp = mock(StaplerResponse2.class); + + when(req.getRestOfPath()).thenReturn("/testCommand"); + + doAnswer( + (Answer) invocation -> { + int statusCode = invocation.getArgument(0); + String message = invocation.getArgument(1); + assertEquals("Expected 404 error", HttpServletResponse.SC_NOT_FOUND, statusCode); + assertTrue("Expected 'No such command' message", message.contains("No such command")); + return null; + }) + .when(rsp) + .sendError(eq(HttpServletResponse.SC_NOT_FOUND), anyString()); + + cliAction.doCommand(req, rsp); + + verify(rsp).sendError(HttpServletResponse.SC_NOT_FOUND, "No such command"); + } + + @Test + public void testDoCommandWithParameter() throws ServletException, IOException { + Jenkins jenkins = jenkinsRule.getInstance(); + + jenkins.setSecurityRealm(jenkinsRule.createDummySecurityRealm()); + jenkinsRule.jenkins.setAuthorizationStrategy( + new MockAuthorizationStrategy() + .grant(Jenkins.READ) + .everywhere() + .to("user")); + + StaplerRequest2 req = mock(StaplerRequest2.class); + StaplerResponse2 rsp = mock(StaplerResponse2.class); + RequestDispatcher dispatcher = mock(RequestDispatcher.class); + + when(req.getRestOfPath()).thenReturn("/add-job-to-view"); + when(req.getView(Optional.ofNullable(any()), anyString())).thenReturn(dispatcher); + + doNothing().when(dispatcher).forward(any(), any()); + + cliAction.doCommand(req, rsp); + + verify(req).getRestOfPath(); + verify(req).getView(Optional.ofNullable(any()), anyString()); + verify(dispatcher).forward(any(), any()); + } + + @Test + public void getIconFileName() { + assertNull(cliAction.getIconFileName()); + } + + @Test + public void getDisplayName() { + assertEquals("Jenkins CLI", cliAction.getDisplayName()); + } + + @Test + public void getUrlName() { + assertEquals("cli", cliAction.getUrlName()); + } + + @Test + public void isWebSocketSupported() { + assertFalse(cliAction.isWebSocketSupported()); + } + + @Test + public void testWebSocketNotSupported() throws Exception { + try (MockedStatic webSocketsMock = Mockito.mockStatic(WebSockets.class)) { + StaplerRequest2 req = mock(StaplerRequest2.class); + + webSocketsMock.when(WebSockets::isSupported).thenReturn(false); + + HttpResponse response = cliAction.doWs(req); + + assertNotNull("Response should not be null", response); + + StaplerResponse2 resp = mock(StaplerResponse2.class); + response.generateResponse(req, resp, null); + + verify(resp).setStatus(HttpServletResponse.SC_NOT_FOUND); + } + } + + @Test + public void testInvalidOrigin() throws Exception { + try (MockedStatic webSocketsMock = Mockito.mockStatic(WebSockets.class); + MockedStatic jenkinsMock = Mockito.mockStatic(Jenkins.class)) { + + StaplerRequest2 req = mock(StaplerRequest2.class); + StaplerResponse2 resp = mock(StaplerResponse2.class); + + webSocketsMock.when(WebSockets::isSupported).thenReturn(true); + + when(req.getHeader("Origin")).thenReturn("https://invalid-origin.com"); + + Jenkins jenkins = mock(Jenkins.class); + jenkinsMock.when(Jenkins::get).thenReturn(jenkins); + when(jenkins.getRootUrlFromRequest()).thenReturn("https://correct-origin.com/"); + when(req.getContextPath()).thenReturn(""); + + Field allowWebSocketField = CLIAction.class.getDeclaredField("ALLOW_WEBSOCKET"); + allowWebSocketField.setAccessible(true); + allowWebSocketField.set(cliAction, null); + + HttpResponse response = cliAction.doWs(req); + + assertNotNull("Response should not be null", response); + + response.generateResponse(req, resp, null); + + verify(resp).setStatus(HttpServletResponse.SC_FORBIDDEN); + } + } + + @Test + public void testWebSocketSupportedAndAllowed() throws Exception { + try (MockedStatic webSocketsMock = Mockito.mockStatic(WebSockets.class); + MockedStatic jenkinsMock = Mockito.mockStatic(Jenkins.class)) { + + webSocketsMock.when(() -> WebSockets.upgrade(any(WebSocketSession.class))) + .thenReturn(mock(HttpResponse.class)); + + StaplerRequest2 req = mock(StaplerRequest2.class); + + webSocketsMock.when(WebSockets::isSupported).thenReturn(true); + + when(req.getHeader("Origin")).thenReturn("https://correct-origin.com"); + + Jenkins jenkins = mock(Jenkins.class); + jenkinsMock.when(Jenkins::get).thenReturn(jenkins); + when(jenkins.getRootUrlFromRequest()).thenReturn("https://correct-origin.com/"); + when(req.getContextPath()).thenReturn(""); + + Field allowWebSocketField = CLIAction.class.getDeclaredField("ALLOW_WEBSOCKET"); + allowWebSocketField.setAccessible(true); + allowWebSocketField.set(cliAction, true); + + HttpResponse response = cliAction.doWs(req); + + assertNotNull("Response should not be null", response); + } + } + + @Test + public void testWebSocketDisabled() throws Exception { + try (MockedStatic webSocketsMock = Mockito.mockStatic(WebSockets.class)) { + StaplerRequest2 req = mock(StaplerRequest2.class); + StaplerResponse2 resp = mock(StaplerResponse2.class); + + webSocketsMock.when(WebSockets::isSupported).thenReturn(true); + + Field allowWebSocketField = CLIAction.class.getDeclaredField("ALLOW_WEBSOCKET"); + allowWebSocketField.setAccessible(true); + allowWebSocketField.set(cliAction, false); + + HttpResponse response = cliAction.doWs(req); + + assertNotNull("Response should not be null", response); + + response.generateResponse(req, resp, null); + + verify(resp).setStatus(HttpServletResponse.SC_FORBIDDEN); + } + } +}