From 883e057587a3da9b9d2c978cc4d301b646b4bd3d Mon Sep 17 00:00:00 2001 From: "Kevin S. Clarke" Date: Thu, 12 Dec 2024 22:27:01 -0500 Subject: [PATCH] Webrepl refactor (#227) * Refactor packages * Add sonar suggested improvements --- .../info/freelibrary/iiif/webrepl/Server.java | 20 +-- .../freelibrary/iiif/webrepl/ServerTest.java | 130 ++++++++++++++---- 2 files changed, 112 insertions(+), 38 deletions(-) diff --git a/src/main/java/info/freelibrary/iiif/webrepl/Server.java b/src/main/java/info/freelibrary/iiif/webrepl/Server.java index 56f5b43f..ab9a1dcb 100644 --- a/src/main/java/info/freelibrary/iiif/webrepl/Server.java +++ b/src/main/java/info/freelibrary/iiif/webrepl/Server.java @@ -66,9 +66,6 @@ public final class Server { /** The server's event loop. **/ private final EventLoop myEventLoop; - /** The event loop handler. **/ - private final Handler myHandler; - /** * Creates a new server instance. * @@ -77,18 +74,21 @@ public final class Server { * @throws URISyntaxException If an invalid URI is passed to the server configuration */ public Server() throws IOException, URISyntaxException, ClassNotFoundException { - myHandler = new JPv3Handler(); - myEventLoop = new EventLoop(getOptions(), myHandler); + myEventLoop = new EventLoop(getOptions(), new Server.JPv3Handler()); myEventLoop.start(); } /** - * Returns the event loop handler. + * Creates a new server instance. * - * @return The event loop handler + * @param aHandler A handler that can handle server events + * @throws ClassNotFoundException If a handler cannot be instantiated + * @throws IOException If there is an error while the server is reading or writing + * @throws URISyntaxException If an invalid URI is passed to the server configuration */ - public Handler getHandler() { - return myHandler; + public Server(final Handler aHandler) throws IOException { + myEventLoop = new EventLoop(getOptions(), aHandler); + myEventLoop.start(); } /** @@ -140,7 +140,7 @@ public static void main(final String[] anArgsArray) /** * An event handler for code evaluation requests. */ - private static class JPv3Handler implements Handler { + static class JPv3Handler implements Handler { /** The delimiter that indicates a submitted code block. */ private static final String CODE_DELIM = "code="; diff --git a/src/test/java/info/freelibrary/iiif/webrepl/ServerTest.java b/src/test/java/info/freelibrary/iiif/webrepl/ServerTest.java index ced70957..c9a8f808 100644 --- a/src/test/java/info/freelibrary/iiif/webrepl/ServerTest.java +++ b/src/test/java/info/freelibrary/iiif/webrepl/ServerTest.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.net.URISyntaxException; +import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -17,9 +18,7 @@ import java.util.concurrent.TimeoutException; import java.util.function.Consumer; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.microhttp.Handler; import org.microhttp.Options; import org.microhttp.Request; import org.microhttp.Response; @@ -27,50 +26,120 @@ import org.mockito.Mockito; import info.freelibrary.util.warnings.JDK; +import info.freelibrary.iiif.webrepl.Server.JPv3Handler; /** * Tests of the {@link Server} class. */ class ServerTest { - /** The server being tested. */ - static Server myServer; + /** The GET method constant. */ + private static final String GET = "GET"; + + /** The POST method constant. */ + private static final String POST = "POST"; + + /** The test code passed to the consumer. */ + private static final String TEST_CODE = "System.out.println(\"Hello, World!\");"; + + /** + * Tests the server's POST response handler. + */ + @Test + @SuppressWarnings(JDK.UNCHECKED) + void testHandlePostSubmitValidCode() throws URISyntaxException, ClassNotFoundException, IOException { + final JPv3Handler handler = new Server.JPv3Handler(); + final Consumer mockConsumer = Mockito.mock(Consumer.class); + final byte[] code = ("code=" + TEST_CODE).getBytes(); + final Request mockRequest = new Request(POST, "/submit", "HTTP/1.1", List.of(), code); + final ArgumentCaptor responseCaptor; + final Response response; + + handler.handle(mockRequest, mockConsumer); + + responseCaptor = ArgumentCaptor.forClass(Response.class); + verify(mockConsumer).accept(responseCaptor.capture()); + response = responseCaptor.getValue(); + + assertEquals(201, response.status()); + assertEquals("OK", response.reason()); + assertEquals("text/plain", response.headers().get(0).value()); + assertEquals("Hello, World!\n", new String(response.body())); + } + + /** + * Tests the server's GET response handler. + */ + @Test + @SuppressWarnings(JDK.UNCHECKED) + final void testHandlerBadMethod() throws URISyntaxException, ClassNotFoundException, IOException { + final Request mockRequest = Mockito.mock(Request.class); + final Consumer mockConsumer = Mockito.mock(Consumer.class); + final ArgumentCaptor responseCaptor; + final Response response; + + when(mockRequest.uri()).thenReturn("http://0.0.0.0/yada"); + when(mockRequest.method()).thenReturn("DELETE"); + when(mockRequest.body()).thenReturn(new byte[] {}); + + new Server.JPv3Handler().handle(mockRequest, mockConsumer); + + // Capture the Response passed to the Consumer + responseCaptor = ArgumentCaptor.forClass(Response.class); + verify(mockConsumer).accept(responseCaptor.capture()); + response = responseCaptor.getValue(); + + assertEquals(405, response.status()); + } /** * Tests the server's response handler. */ @Test @SuppressWarnings(JDK.UNCHECKED) - final void testHandlerGet() { - final Handler handler = myServer.getHandler(); + final void testHandlerGet() throws URISyntaxException, ClassNotFoundException, IOException { final Request mockRequest = Mockito.mock(Request.class); final Consumer mockConsumer = Mockito.mock(Consumer.class); final ArgumentCaptor responseCaptor; final Response response; - when(mockRequest.uri()).thenReturn("http://0.0.0.0/editor"); - when(mockRequest.method()).thenReturn("GET"); + when(mockRequest.uri()).thenReturn("http://0.0.0.0/"); + when(mockRequest.method()).thenReturn(GET); when(mockRequest.body()).thenReturn(new byte[] {}); - handler.handle(mockRequest, mockConsumer); + new Server.JPv3Handler().handle(mockRequest, mockConsumer); // Capture the Response passed to the Consumer responseCaptor = ArgumentCaptor.forClass(Response.class); verify(mockConsumer).accept(responseCaptor.capture()); response = responseCaptor.getValue(); - System.out.println(response.status()); + assertEquals(404, response.status()); } /** - * Tests the retrieval of the server options. + * Tests the server's GET response handler. */ @Test - final void testOptions() { - final Options opts = myServer.getOptions(); + @SuppressWarnings(JDK.UNCHECKED) + final void testHandlerGetEditor() throws URISyntaxException, ClassNotFoundException, IOException { + final Request mockRequest = Mockito.mock(Request.class); + final Consumer mockConsumer = Mockito.mock(Consumer.class); + final ArgumentCaptor responseCaptor; + final Response response; - assertEquals(INADDR_ANY, opts.host()); - assertEquals(Integer.parseInt(System.getenv(Config.HTTP_PORT)), opts.port()); + when(mockRequest.uri()).thenReturn("http://0.0.0.0/editor"); + when(mockRequest.method()).thenReturn(GET); + when(mockRequest.body()).thenReturn(new byte[] {}); + + new Server.JPv3Handler().handle(mockRequest, mockConsumer); + + // Capture the Response passed to the Consumer + responseCaptor = ArgumentCaptor.forClass(Response.class); + verify(mockConsumer).accept(responseCaptor.capture()); + response = responseCaptor.getValue(); + + assertEquals(201, response.status()); } /** @@ -79,12 +148,13 @@ final void testOptions() { * @throws InterruptedException If the server cannot be started */ @Test - final void testServer() throws InterruptedException { + final void testServer() throws InterruptedException, ClassNotFoundException, URISyntaxException, IOException { final ExecutorService executor = Executors.newSingleThreadExecutor(); + final Server server = new Server(); final Runnable task = () -> { try { - myServer.run(); + server.run(); } catch (final InterruptedException details) { Thread.currentThread().interrupt(); fail(details.getMessage(), details); @@ -96,7 +166,7 @@ final void testServer() throws InterruptedException { future.get(2, TimeUnit.SECONDS); } catch (TimeoutException | ExecutionException | InterruptedException details) { - myServer.stop(); + server.stop(); if (!(details instanceof TimeoutException)) { fail(details.getMessage()); @@ -107,7 +177,7 @@ final void testServer() throws InterruptedException { try { if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { - myServer.stop(); + server.stop(); } } catch (final InterruptedException details) { Thread.currentThread().interrupt(); @@ -116,16 +186,20 @@ final void testServer() throws InterruptedException { } /** - * Sets up a new test server. + * Tests the server constructor that takes a event loop handler. * - * @throws ClassNotFoundException If the handler class cannot be found - * @throws URISyntaxException If the setup uses an invalid URI - * @throws InterruptedException If the server gets interrupted in an unexpected way - * @throws IOException If there is trouble reading and writing from the server + * @throws ClassNotFoundException If the handler cannot be found + * @throws URISyntaxException If the handler uses an invalid URI + * @throws IOException If there is trouble reading or writing from the server */ - @BeforeAll - static final void setUp() throws ClassNotFoundException, URISyntaxException, IOException { - myServer = new Server(); - } + @Test + final void testServerWithHandler() throws ClassNotFoundException, URISyntaxException, IOException { + final Server server = new Server(new Server.JPv3Handler()); + final Options opts = server.getOptions(); + assertEquals(INADDR_ANY, opts.host()); + assertEquals(Integer.parseInt(System.getenv(Config.HTTP_PORT)), opts.port()); + + server.stop(); + } }