From 63e3f418ff21f9de566cf9d8e601dc97e08fa089 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 00:38:23 -0700 Subject: [PATCH 01/28] feat: add Server::stop() --- src/main/java/edu/byu/cs/server/Server.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/edu/byu/cs/server/Server.java b/src/main/java/edu/byu/cs/server/Server.java index 5e92321c..11c49c93 100644 --- a/src/main/java/edu/byu/cs/server/Server.java +++ b/src/main/java/edu/byu/cs/server/Server.java @@ -4,6 +4,7 @@ import edu.byu.cs.server.endpointprovider.EndpointProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import spark.Spark; import static spark.Spark.*; @@ -22,6 +23,10 @@ public int start(int desiredPort) { return chosenPort; } + public void stop() { + Spark.stop(); + } + private int setupEndpoints(int port) { port(port); From 2bfe27f2eef1f1c4646cc7b48a1ec70a77d72875 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 00:41:12 -0700 Subject: [PATCH 02/28] feat: add TestServerFacade (a simple client for testing Server endpoints) --- .../edu/byu/cs/server/TestServerFacade.java | 112 ++++++++++++++++++ .../exception/ResponseParseException.java | 7 ++ .../exception/ServerConnectionException.java | 7 ++ 3 files changed, 126 insertions(+) create mode 100644 src/test/java/edu/byu/cs/server/TestServerFacade.java create mode 100644 src/test/java/edu/byu/cs/server/exception/ResponseParseException.java create mode 100644 src/test/java/edu/byu/cs/server/exception/ServerConnectionException.java diff --git a/src/test/java/edu/byu/cs/server/TestServerFacade.java b/src/test/java/edu/byu/cs/server/TestServerFacade.java new file mode 100644 index 00000000..5c83ddfd --- /dev/null +++ b/src/test/java/edu/byu/cs/server/TestServerFacade.java @@ -0,0 +1,112 @@ +package edu.byu.cs.server; + +import com.google.gson.Gson; +import edu.byu.cs.server.exception.ResponseParseException; +import edu.byu.cs.server.exception.ServerConnectionException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Map; + +public class TestServerFacade { + private final String serverURL; + + public TestServerFacade(String serverURL, int port) { + this.serverURL = "http://%s:%d".formatted(serverURL, port); + } + + public Object makeRequest(String method, String path) throws IOException, ServerConnectionException, ResponseParseException { + return makeRequest(method, path, null, null, Object.class); + } + + public T makeRequest(String method, String path, Object request, Map headers, + Class responseClass) throws IOException, ServerConnectionException, ResponseParseException { + HttpURLConnection http = getConnection(method, serverURL + path); + writeRequest(http, request, headers); + connect(http); + return readResponse(http, responseClass); + } + + private HttpURLConnection getConnection(String method, String urlString) throws ServerConnectionException { + try { + URL url = (new URI(urlString)).toURL(); + HttpURLConnection http = (HttpURLConnection) url.openConnection(); + http.setRequestMethod(method); + http.setDoOutput("POST".equals(method) || "PUT".equals(method)); + return http; + } catch (IOException | URISyntaxException e) { + throw new ServerConnectionException("Failed to set up HTTP connection: " + e.getMessage()); + } + } + + private void writeRequest(HttpURLConnection http, Object requestBody, Map headers) throws ServerConnectionException { + try { + writeHeaders(http, headers); + writeRequestBody(http, requestBody); + } catch (IOException e) { + throw new ServerConnectionException("Could not write request body: " + e.getMessage()); + } + } + + private void connect(HttpURLConnection http) throws ServerConnectionException { + try { + http.connect(); + } catch (IOException e) { + throw new ServerConnectionException("Failed to connect to server: " + e.getMessage()); + } + } + + private T readResponse(HttpURLConnection http, Class responseClass) throws IOException, + ResponseParseException { + String respString = getRespString(http); + try { + return new Gson().fromJson(respString, responseClass); + } catch (Exception e) { + String message = String.format("Error parsing response. Expected JSON, got '%s'", respString); + throw new ResponseParseException(message, e); + } + } + + private void writeHeaders(HttpURLConnection http, Map headers) { + http.addRequestProperty("Content-type", "application/json"); + if (headers != null) { + for (String headerName : headers.keySet()) { + String headerValue = headers.get(headerName); + http.addRequestProperty(headerName, headerValue); + } + } + } + + private void writeRequestBody(HttpURLConnection http, Object request) throws IOException { + if (request != null) { + String requestData = new Gson().toJson(request); + try (OutputStream reqBody = http.getOutputStream()) { + reqBody.write(requestData.getBytes()); + } + } + } + + private String getRespString(HttpURLConnection http) throws IOException { + InputStream respBody; + if (http.getResponseCode() / 100 == 2) { + respBody = http.getInputStream(); + } else { + respBody = http.getErrorStream(); + } + StringBuilder sb = new StringBuilder(); + InputStreamReader sr = new InputStreamReader(respBody); + char[] buf = new char[1024]; + int len; + while ((len = sr.read(buf)) > 0) { + sb.append(buf, 0, len); + } + respBody.close(); + return sb.toString(); + } +} diff --git a/src/test/java/edu/byu/cs/server/exception/ResponseParseException.java b/src/test/java/edu/byu/cs/server/exception/ResponseParseException.java new file mode 100644 index 00000000..366ab2ee --- /dev/null +++ b/src/test/java/edu/byu/cs/server/exception/ResponseParseException.java @@ -0,0 +1,7 @@ +package edu.byu.cs.server.exception; + +public class ResponseParseException extends Exception { + public ResponseParseException(String message, Exception cause) { + super(message, cause); + } +} diff --git a/src/test/java/edu/byu/cs/server/exception/ServerConnectionException.java b/src/test/java/edu/byu/cs/server/exception/ServerConnectionException.java new file mode 100644 index 00000000..7340f4cb --- /dev/null +++ b/src/test/java/edu/byu/cs/server/exception/ServerConnectionException.java @@ -0,0 +1,7 @@ +package edu.byu.cs.server.exception; + +public class ServerConnectionException extends Exception { + public ServerConnectionException(String message) { + super(message); + } +} From b56d696530d1aabc905ca693943562d29ca387d3 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 00:58:02 -0700 Subject: [PATCH 03/28] test: add MockEndpointProvider class --- .../MockEndpointProvider.java | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java diff --git a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java new file mode 100644 index 00000000..398a180f --- /dev/null +++ b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java @@ -0,0 +1,196 @@ +package edu.byu.cs.server.endpointprovider; + +import spark.Filter; +import spark.Request; +import spark.Response; +import spark.Route; + +/** + * A mock implementation of EndpointProvider designed for use with + * Mockito.mock(). Because individual endpoint methods only run once at + * Server startup, they aren't useful for verifying endpoint calls. + * MockEndpointProvider provides a runHandler() method that can be used for + * that purpose. + */ +public class MockEndpointProvider implements EndpointProvider { + + /** + * An empty function that is run by every endpoint, designed for use in + * Mockito.verify() to verify endpoint calls. The individual endpoint + * methods only run once on Server startup, so they can't be used + * for that purpose. + * + * @param name the name of the Route that was called + * @param req the Request object passed into the Route + * @param res the Response object passed into the Route + * @return null + */ + public Object runHandler(String name, Request req, Response res) { + return null; + } + + @Override + public Filter beforeAll() { + return (req, res) -> runHandler("beforeAll", req, res); + } + + @Override + public Filter afterAll() { + return (req, res) -> runHandler("afterAll", req, res); + } + + @Override + public Route defaultGet() { + return (req, res) -> runHandler("defaultGet", req, res); + } + + @Override + public Route usersGet() { + return (req, res) -> runHandler("usersGet", req, res); + } + + @Override + public Route testModeGet() { + return (req, res) -> runHandler("testModeGet", req, res); + } + + @Override + public Route commitAnalyticsGet() { + return (req, res) -> runHandler("commitAnalyticsGet", req, res); + } + + @Override + public Route honorCheckerZipGet() { + return (req, res) -> runHandler("honorCheckerZipGet", req, res); + } + + @Override + public Route sectionsGet() { + return (req, res) -> runHandler("sectionsGet", req, res); + } + + @Override + public Filter verifyAuthenticatedMiddleware() { + return (req, res) -> runHandler("verifyAuthenticatedMiddleware", req, res); + } + + @Override + public Filter verifyAdminMiddleware() { + return (req, res) -> runHandler("verifyAdminMiddleware", req, res); + } + + @Override + public Route meGet() { + return (req, res) -> runHandler("meGet", req, res); + } + + @Override + public Route callbackGet() { + return (req, res) -> runHandler("callbackGet", req, res); + } + + @Override + public Route loginGet() { + return (req, res) -> runHandler("loginGet", req, res); + } + + @Override + public Route logoutPost() { + return (req, res) -> runHandler("logoutPost", req, res); + } + + @Override + public Route getConfigAdmin() { + return (req, res) -> runHandler("getConfigAdmin", req, res); + } + + @Override + public Route getConfigStudent() { + return (req, res) -> runHandler("getConfigStudent", req, res); + } + + @Override + public Route updateLivePhases() { + return (req, res) -> runHandler("updateLivePhases", req, res); + } + + @Override + public Route updateBannerMessage() { + return (req, res) -> runHandler("updateBannerMessage", req, res); + } + + @Override + public Route updateCourseIdsPost() { + return (req, res) -> runHandler("updateCourseIdsPost", req, res); + } + + @Override + public Route updateCourseIdsUsingCanvasGet() { + return (req, res) -> runHandler("updateCourseIdsUsingCanvasGet", req, res); + } + + @Override + public Route submitPost() { + return (req, res) -> runHandler("submitPost", req, res); + } + + @Override + public Route adminRepoSubmitPost() { + return (req, res) -> runHandler("adminRepoSubmitPost", req, res); + } + + @Override + public Route submitGet() { + return (req, res) -> runHandler("submitGet", req, res); + } + + @Override + public Route latestSubmissionForMeGet() { + return (req, res) -> runHandler("latestSubmissionForMeGet", req, res); + } + + @Override + public Route submissionXGet() { + return (req, res) -> runHandler("submissionXGet", req, res); + } + + @Override + public Route latestSubmissionsGet() { + return (req, res) -> runHandler("latestSubmissionsGet", req, res); + } + + @Override + public Route submissionsActiveGet() { + return (req, res) -> runHandler("submissionsActiveGet", req, res); + } + + @Override + public Route studentSubmissionsGet() { + return (req, res) -> runHandler("studentSubmissionsGet", req, res); + } + + @Override + public Route approveSubmissionPost() { + return (req, res) -> runHandler("approveSubmissionPost", req, res); + } + + @Override + public Route submissionsReRunPost() { + return (req, res) -> runHandler("submissionsReRunPost", req, res); + } + + @Override + public Route repoPatch() { + return (req, res) -> runHandler("repoPatch", req, res); + } + + @Override + public Route repoPatchAdmin() { + return (req, res) -> runHandler("repoPatchAdmin", req, res); + } + + @Override + public Route repoHistoryAdminGet() { + return (req, res) -> runHandler("repoHistoryAdminGet", req, res); + } +} From 7f81ef6b32ffb41ae19661c86c1bc992e7fb56fc Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 00:59:19 -0700 Subject: [PATCH 04/28] test: add ServerTest --- .../java/edu/byu/cs/server/ServerTest.java | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 src/test/java/edu/byu/cs/server/ServerTest.java diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java new file mode 100644 index 00000000..6e7a4e5b --- /dev/null +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -0,0 +1,154 @@ +package edu.byu.cs.server; + +import edu.byu.cs.server.endpointprovider.MockEndpointProvider; +import edu.byu.cs.server.exception.ResponseParseException; +import edu.byu.cs.server.exception.ServerConnectionException; +import org.junit.jupiter.api.*; +import org.mockito.InOrder; + +import java.io.IOException; +import java.util.*; + +import static org.mockito.Mockito.*; + +class ServerTest { + private static TestServerFacade serverFacade; + private static Server server; + + private static final MockEndpointProvider mockedMockProvider = spy(new MockEndpointProvider()); + + // TODO verify middleware + // TODO figure out how to test PATCH calls... HttpURLConnection thinks it's an invalid method + // TODO verify endpoints that take pathParams + // TODO verify whether authentication is required + + @AfterAll + static void stopServer() { + server.stop(); + } + + @BeforeAll + public static void init() { + server = new Server(mockedMockProvider); + int port = server.start(0); + System.out.println("Started test HTTP server on " + port); + + serverFacade = new TestServerFacade("localhost", port); + } + + @AfterEach + public void tearDown() { + reset(mockedMockProvider); + } + + @Test + void method_GET_endpoints_call_their_handlers_exactly_once() { + String[][] endpoints = { + {"callbackGet", "/auth/callback"}, + {"loginGet", "/auth/login"}, + {"usersGet", "/api/admin/users"}, + {"testModeGet", "/api/admin/test_mode"}, + {"commitAnalyticsGet", "/api/admin/analytics/commit"}, // TODO: test with {option} +// {"honorCheckerZipGet", "/api/admin/honorChecker/zip/{section}"}, // TODO: requires {section} + {"sectionsGet", "/api/admin/sections"}, + {"meGet", "/api/me"}, + {"getConfigAdmin", "/api/admin/config"}, + {"getConfigStudent", "/api/config"}, + {"updateCourseIdsUsingCanvasGet", "/api/admin/config/courseIds"}, + {"submitGet", "/api/submit"}, + {"latestSubmissionForMeGet", "/api/latest"}, + {"submissionXGet", "/api/submission"}, // TODO test with {phase} + {"latestSubmissionsGet", "/api/admin/submissions/latest"}, // TODO: test with {count} + {"submissionsActiveGet", "/api/admin/submissions/active"}, +// {"studentSubmissionsGet", "/admin/submissions/student/{netId}"}, // TODO requires {netId} + {"repoHistoryAdminGet", "/api/admin/repo/history"} + }; + + verifyEndpointsCallTheirHandlersExactlyOnce("GET", endpoints); + } + + @Test + void method_POST_endpoints_call_their_handlers_exactly_once() { + String[][] endpoints = { + {"logoutPost", "/auth/logout"}, + {"updateLivePhases", "/api/admin/config/phases"}, + {"updateBannerMessage", "/api/admin/config/banner"}, + {"updateCourseIdsPost", "/api/admin/config/courseIds"}, + {"submitPost", "/api/submit"}, + {"adminRepoSubmitPost", "/api/admin/submit"}, + {"approveSubmissionPost", "/api/admin/submissions/approve"}, + {"submissionsReRunPost", "/api/admin/submissions/rerun"} + }; + + verifyEndpointsCallTheirHandlersExactlyOnce("POST", endpoints); + } + + private void verifyEndpointsCallTheirHandlersExactlyOnce(String method, String[][] endpoints) { + Set failingEndpoints = new HashSet<>(); + for (String[] endpoint : endpoints) { + String endpointName = endpoint[0]; + String path = endpoint[1]; + try { + + serverFacade.makeRequest(method, path); // When + + verify(mockedMockProvider, times(1)).runHandler(eq(endpointName), any(), any()); // Then + + } + catch (Exception e) { + failingEndpoints.add(endpointName); + } + } + + Assertions.assertEquals(new HashSet<>(), failingEndpoints, + "Not all endpoints had exactly 1 function call."); + } + + @Test + void nonexistent_GET_endpoint_calls_defaultGet_exactly_once() + throws IOException, ServerConnectionException, ResponseParseException { + serverFacade.makeRequest("GET", "/iDoNotExist"); + verify(mockedMockProvider, times(1)).runHandler(eq("defaultGet"), any(), any()); + } + + @Test + void nonexistent_OPTIONS_endpoint_calls_defaultOptions_exactly_once() + throws IOException, ServerConnectionException, ResponseParseException { + serverFacade.makeRequest("OPTIONS", "/iDoNotExist"); + verify(mockedMockProvider, times(1)).runHandler(eq("defaultOptions"), any(), any()); + } + + @Test + void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_handlers_exactly_once_in_order() + throws IOException, ServerConnectionException, ResponseParseException { + serverFacade.makeRequest("GET", "/iDoNotExist"); + + // Verify they ran in order + InOrder inOrder = inOrder(mockedMockProvider); + inOrder.verify(mockedMockProvider).runHandler(eq("beforeAll"), any(), any()); + inOrder.verify(mockedMockProvider).runHandler(eq("defaultGet"), any(), any()); + inOrder.verify(mockedMockProvider).runHandler(eq("afterAll"), any(), any()); + + // Verify they only ran once + verify(mockedMockProvider, times(1)).runHandler(eq("beforeAll"), any(), any()); + verify(mockedMockProvider, times(1)).runHandler(eq("defaultGet"), any(), any()); + verify(mockedMockProvider, times(1)).runHandler(eq("afterAll"), any(), any()); + } + + @Test + void meGet_endpoint_calls_beforeAll_and_afterAll_handlers_exactly_once_in_order() + throws IOException, ServerConnectionException, ResponseParseException { + serverFacade.makeRequest("GET", "/api/me"); + + // Verify they ran in order + InOrder inOrder = inOrder(mockedMockProvider); + inOrder.verify(mockedMockProvider).runHandler(eq("beforeAll"), any(), any()); + inOrder.verify(mockedMockProvider).runHandler(eq("meGet"), any(), any()); + inOrder.verify(mockedMockProvider).runHandler(eq("afterAll"), any(), any()); + + // Verify they only ran once + verify(mockedMockProvider, times(1)).runHandler(eq("beforeAll"), any(), any()); + verify(mockedMockProvider, times(1)).runHandler(eq("meGet"), any(), any()); + verify(mockedMockProvider, times(1)).runHandler(eq("afterAll"), any(), any()); + } +} \ No newline at end of file From 3c1463d2cb8ca5a51a785534f47a29c4475b1d12 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 14:59:26 -0700 Subject: [PATCH 05/28] standardize test names --- src/test/java/edu/byu/cs/server/ServerTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 6e7a4e5b..28f53465 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -94,8 +94,7 @@ private void verifyEndpointsCallTheirHandlersExactlyOnce(String method, String[] verify(mockedMockProvider, times(1)).runHandler(eq(endpointName), any(), any()); // Then - } - catch (Exception e) { + } catch (Exception e) { failingEndpoints.add(endpointName); } } @@ -119,7 +118,7 @@ void nonexistent_OPTIONS_endpoint_calls_defaultOptions_exactly_once() } @Test - void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_handlers_exactly_once_in_order() + void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_exactly_once_in_order() throws IOException, ServerConnectionException, ResponseParseException { serverFacade.makeRequest("GET", "/iDoNotExist"); @@ -136,7 +135,7 @@ void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_hand } @Test - void meGet_endpoint_calls_beforeAll_and_afterAll_handlers_exactly_once_in_order() + void meGet_endpoint_calls_beforeAll_then_meGet_then_afterAll_exactly_once_in_order() throws IOException, ServerConnectionException, ResponseParseException { serverFacade.makeRequest("GET", "/api/me"); From 7521e6aa8bf004080223bebb01d7248df1d8a620 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 16:07:45 -0700 Subject: [PATCH 06/28] fix: add awaitInitialization() --- src/main/java/edu/byu/cs/server/Server.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/edu/byu/cs/server/Server.java b/src/main/java/edu/byu/cs/server/Server.java index 11c49c93..d1c19262 100644 --- a/src/main/java/edu/byu/cs/server/Server.java +++ b/src/main/java/edu/byu/cs/server/Server.java @@ -20,6 +20,7 @@ public Server(EndpointProvider endpointProvider) { public int start(int desiredPort) { int chosenPort = setupEndpoints(desiredPort); LOGGER.info("Server started on port {}", chosenPort); + awaitInitialization(); return chosenPort; } From 27308ea6958a4bdb1f7903dc160d65d92713c00c Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 16:08:31 -0700 Subject: [PATCH 07/28] use port 8080, not 0 --- src/test/java/edu/byu/cs/server/ServerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 28f53465..1ef4ea65 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -30,7 +30,7 @@ static void stopServer() { @BeforeAll public static void init() { server = new Server(mockedMockProvider); - int port = server.start(0); + int port = server.start(8080); System.out.println("Started test HTTP server on " + port); serverFacade = new TestServerFacade("localhost", port); From bd6104f8a2811948da8cef6f3b3a2b6d238e686a Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 16:34:40 -0700 Subject: [PATCH 08/28] fix: return a valid JSON response from MockEndpointProvider Otherwise, JavaSpark intervenes and returns a 404 HTML page. --- .../byu/cs/server/endpointprovider/MockEndpointProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java index 398a180f..3d986a13 100644 --- a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java +++ b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java @@ -26,7 +26,7 @@ public class MockEndpointProvider implements EndpointProvider { * @return null */ public Object runHandler(String name, Request req, Response res) { - return null; + return "{}"; } @Override From d6b6e862e8b33742ff369449b9a193ff76be27c7 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 16:39:58 -0700 Subject: [PATCH 09/28] fix: remove irrelevant test It was meant to be in a different branch. --- src/test/java/edu/byu/cs/server/ServerTest.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 1ef4ea65..0e7c3fa7 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -110,13 +110,6 @@ void nonexistent_GET_endpoint_calls_defaultGet_exactly_once() verify(mockedMockProvider, times(1)).runHandler(eq("defaultGet"), any(), any()); } - @Test - void nonexistent_OPTIONS_endpoint_calls_defaultOptions_exactly_once() - throws IOException, ServerConnectionException, ResponseParseException { - serverFacade.makeRequest("OPTIONS", "/iDoNotExist"); - verify(mockedMockProvider, times(1)).runHandler(eq("defaultOptions"), any(), any()); - } - @Test void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_exactly_once_in_order() throws IOException, ServerConnectionException, ResponseParseException { From 4aabb81c65fba475026d4e2a13c53541d540a4ac Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 17:10:59 -0700 Subject: [PATCH 10/28] test: verify beforeAll and afterAll are called, in the right order, for every endpoint --- .../java/edu/byu/cs/server/ServerTest.java | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 0e7c3fa7..3fc4d798 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -64,7 +64,7 @@ void method_GET_endpoints_call_their_handlers_exactly_once() { {"repoHistoryAdminGet", "/api/admin/repo/history"} }; - verifyEndpointsCallTheirHandlersExactlyOnce("GET", endpoints); + verifyEndpointsCallTheirHandlersExactlyOnceInOrder("GET", endpoints); } @Test @@ -80,20 +80,16 @@ void method_POST_endpoints_call_their_handlers_exactly_once() { {"submissionsReRunPost", "/api/admin/submissions/rerun"} }; - verifyEndpointsCallTheirHandlersExactlyOnce("POST", endpoints); + verifyEndpointsCallTheirHandlersExactlyOnceInOrder("POST", endpoints); } - private void verifyEndpointsCallTheirHandlersExactlyOnce(String method, String[][] endpoints) { + private void verifyEndpointsCallTheirHandlersExactlyOnceInOrder(String method, String[][] endpoints) { Set failingEndpoints = new HashSet<>(); for (String[] endpoint : endpoints) { String endpointName = endpoint[0]; String path = endpoint[1]; try { - - serverFacade.makeRequest(method, path); // When - - verify(mockedMockProvider, times(1)).runHandler(eq(endpointName), any(), any()); // Then - + verifyEndpointCallsItsHandlersExactlyOnceInOrder(endpointName, method, path); } catch (Exception e) { failingEndpoints.add(endpointName); } @@ -103,6 +99,18 @@ private void verifyEndpointsCallTheirHandlersExactlyOnce(String method, String[] "Not all endpoints had exactly 1 function call."); } + private void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String endpointName, String method, String path) + throws ServerConnectionException, ResponseParseException, IOException { + // When + serverFacade.makeRequest(method, path); + + // Then + InOrder inOrder = inOrder(mockedMockProvider); + inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("beforeAll"), any(), any()); + inOrder.verify(mockedMockProvider, times(1)).runHandler(eq(endpointName), any(), any()); + inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("afterAll"), any(), any()); + } + @Test void nonexistent_GET_endpoint_calls_defaultGet_exactly_once() throws IOException, ServerConnectionException, ResponseParseException { @@ -126,21 +134,4 @@ void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_exac verify(mockedMockProvider, times(1)).runHandler(eq("defaultGet"), any(), any()); verify(mockedMockProvider, times(1)).runHandler(eq("afterAll"), any(), any()); } - - @Test - void meGet_endpoint_calls_beforeAll_then_meGet_then_afterAll_exactly_once_in_order() - throws IOException, ServerConnectionException, ResponseParseException { - serverFacade.makeRequest("GET", "/api/me"); - - // Verify they ran in order - InOrder inOrder = inOrder(mockedMockProvider); - inOrder.verify(mockedMockProvider).runHandler(eq("beforeAll"), any(), any()); - inOrder.verify(mockedMockProvider).runHandler(eq("meGet"), any(), any()); - inOrder.verify(mockedMockProvider).runHandler(eq("afterAll"), any(), any()); - - // Verify they only ran once - verify(mockedMockProvider, times(1)).runHandler(eq("beforeAll"), any(), any()); - verify(mockedMockProvider, times(1)).runHandler(eq("meGet"), any(), any()); - verify(mockedMockProvider, times(1)).runHandler(eq("afterAll"), any(), any()); - } } \ No newline at end of file From 2ca3594c08b676b0dda84168ad6f5a8cff5f755d Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 17:45:19 -0700 Subject: [PATCH 11/28] test: verify authentication middleware is called when needed --- .../java/edu/byu/cs/server/ServerTest.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 3fc4d798..9f8dd7a7 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -17,10 +17,8 @@ class ServerTest { private static final MockEndpointProvider mockedMockProvider = spy(new MockEndpointProvider()); - // TODO verify middleware // TODO figure out how to test PATCH calls... HttpURLConnection thinks it's an invalid method // TODO verify endpoints that take pathParams - // TODO verify whether authentication is required @AfterAll static void stopServer() { @@ -93,6 +91,8 @@ private void verifyEndpointsCallTheirHandlersExactlyOnceInOrder(String method, S } catch (Exception e) { failingEndpoints.add(endpointName); } + + reset(mockedMockProvider); } Assertions.assertEquals(new HashSet<>(), failingEndpoints, @@ -107,10 +107,29 @@ private void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String endpointNam // Then InOrder inOrder = inOrder(mockedMockProvider); inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("beforeAll"), any(), any()); + this.verifyInOrder_authenticationMiddleware(path, inOrder); inOrder.verify(mockedMockProvider, times(1)).runHandler(eq(endpointName), any(), any()); inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("afterAll"), any(), any()); } + private void verifyInOrder_authenticationMiddleware(String path, InOrder inOrder) { + List pathNodes = Arrays.stream(path.split("/")).toList(); + + if (!pathNodes.contains("api")) { + return; + } + + if (!pathNodes.contains("auth")) { + // Requires authentication + inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("verifyAuthenticatedMiddleware"), any(), any()); + } + + if (pathNodes.contains("admin")) { + // Requires admin + inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("verifyAdminMiddleware"), any(), any()); + } + } + @Test void nonexistent_GET_endpoint_calls_defaultGet_exactly_once() throws IOException, ServerConnectionException, ResponseParseException { From 9a6edc10750df7a32aa4d350d091ee9a144c491b Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 17:51:39 -0700 Subject: [PATCH 12/28] feat: add junit-jupiter-params dependency (for parameterized tests) --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index d05bcf09..a6585054 100644 --- a/pom.xml +++ b/pom.xml @@ -112,6 +112,12 @@ slf4j-api 2.0.13 + + org.junit.jupiter + junit-jupiter-params + 5.10.0 + test + From d91348bf3f3b9b440830a46e49e19d30bcb05fe1 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 17:51:57 -0700 Subject: [PATCH 13/28] test: change endpoint verification to use parameterized tests rather than loops --- .../java/edu/byu/cs/server/ServerTest.java | 102 +++++++----------- 1 file changed, 40 insertions(+), 62 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 9f8dd7a7..597f0eb0 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -4,10 +4,14 @@ import edu.byu.cs.server.exception.ResponseParseException; import edu.byu.cs.server.exception.ServerConnectionException; import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InOrder; import java.io.IOException; import java.util.*; +import java.util.stream.Stream; import static org.mockito.Mockito.*; @@ -17,6 +21,37 @@ class ServerTest { private static final MockEndpointProvider mockedMockProvider = spy(new MockEndpointProvider()); + public static Stream getEndpoints() { + return Stream.of( + Arguments.of("GET", "callbackGet", "/auth/callback"), + Arguments.of("GET", "loginGet", "/auth/login"), + Arguments.of("GET", "usersGet", "/api/admin/users"), + Arguments.of("GET", "testModeGet", "/api/admin/test_mode"), + Arguments.of("GET", "commitAnalyticsGet", "/api/admin/analytics/commit"), // TODO: test with {option} +// Arguments.of("GET", "honorCheckerZipGet", "/api/admin/honorChecker/zip/{section}"), // TODO: requires {section} + Arguments.of("GET", "sectionsGet", "/api/admin/sections"), + Arguments.of("GET", "meGet", "/api/me"), + Arguments.of("GET", "getConfigAdmin", "/api/admin/config"), + Arguments.of("GET", "getConfigStudent", "/api/config"), + Arguments.of("GET", "updateCourseIdsUsingCanvasGet", "/api/admin/config/courseIds"), + Arguments.of("GET", "submitGet", "/api/submit"), + Arguments.of("GET", "latestSubmissionForMeGet", "/api/latest"), + Arguments.of("GET", "submissionXGet", "/api/submission"), // TODO test with {phase} + Arguments.of("GET", "latestSubmissionsGet", "/api/admin/submissions/latest"), // TODO: test with {count} + Arguments.of("GET", "submissionsActiveGet", "/api/admin/submissions/active"), +// Arguments.of("GET", "studentSubmissionsGet", "/admin/submissions/student/{netId}"), // TODO: requires {netId} + Arguments.of("GET", "repoHistoryAdminGet", "/api/admin/repo/history"), + Arguments.of("POST", "logoutPost", "/auth/logout"), + Arguments.of("POST", "updateLivePhases", "/api/admin/config/phases"), + Arguments.of("POST", "updateBannerMessage", "/api/admin/config/banner"), + Arguments.of("POST", "updateCourseIdsPost", "/api/admin/config/courseIds"), + Arguments.of("POST", "submitPost", "/api/submit"), + Arguments.of("POST", "adminRepoSubmitPost", "/api/admin/submit"), + Arguments.of("POST", "approveSubmissionPost", "/api/admin/submissions/approve"), + Arguments.of("POST", "submissionsReRunPost", "/api/admin/submissions/rerun") + ); + } + // TODO figure out how to test PATCH calls... HttpURLConnection thinks it's an invalid method // TODO verify endpoints that take pathParams @@ -39,73 +74,16 @@ public void tearDown() { reset(mockedMockProvider); } - @Test - void method_GET_endpoints_call_their_handlers_exactly_once() { - String[][] endpoints = { - {"callbackGet", "/auth/callback"}, - {"loginGet", "/auth/login"}, - {"usersGet", "/api/admin/users"}, - {"testModeGet", "/api/admin/test_mode"}, - {"commitAnalyticsGet", "/api/admin/analytics/commit"}, // TODO: test with {option} -// {"honorCheckerZipGet", "/api/admin/honorChecker/zip/{section}"}, // TODO: requires {section} - {"sectionsGet", "/api/admin/sections"}, - {"meGet", "/api/me"}, - {"getConfigAdmin", "/api/admin/config"}, - {"getConfigStudent", "/api/config"}, - {"updateCourseIdsUsingCanvasGet", "/api/admin/config/courseIds"}, - {"submitGet", "/api/submit"}, - {"latestSubmissionForMeGet", "/api/latest"}, - {"submissionXGet", "/api/submission"}, // TODO test with {phase} - {"latestSubmissionsGet", "/api/admin/submissions/latest"}, // TODO: test with {count} - {"submissionsActiveGet", "/api/admin/submissions/active"}, -// {"studentSubmissionsGet", "/admin/submissions/student/{netId}"}, // TODO requires {netId} - {"repoHistoryAdminGet", "/api/admin/repo/history"} - }; - - verifyEndpointsCallTheirHandlersExactlyOnceInOrder("GET", endpoints); - } - - @Test - void method_POST_endpoints_call_their_handlers_exactly_once() { - String[][] endpoints = { - {"logoutPost", "/auth/logout"}, - {"updateLivePhases", "/api/admin/config/phases"}, - {"updateBannerMessage", "/api/admin/config/banner"}, - {"updateCourseIdsPost", "/api/admin/config/courseIds"}, - {"submitPost", "/api/submit"}, - {"adminRepoSubmitPost", "/api/admin/submit"}, - {"approveSubmissionPost", "/api/admin/submissions/approve"}, - {"submissionsReRunPost", "/api/admin/submissions/rerun"} - }; - - verifyEndpointsCallTheirHandlersExactlyOnceInOrder("POST", endpoints); - } - - private void verifyEndpointsCallTheirHandlersExactlyOnceInOrder(String method, String[][] endpoints) { - Set failingEndpoints = new HashSet<>(); - for (String[] endpoint : endpoints) { - String endpointName = endpoint[0]; - String path = endpoint[1]; - try { - verifyEndpointCallsItsHandlersExactlyOnceInOrder(endpointName, method, path); - } catch (Exception e) { - failingEndpoints.add(endpointName); - } - - reset(mockedMockProvider); - } - - Assertions.assertEquals(new HashSet<>(), failingEndpoints, - "Not all endpoints had exactly 1 function call."); - } - - private void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String endpointName, String method, String path) + @ParameterizedTest + @MethodSource("getEndpoints") + public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, String endpointName, String path) throws ServerConnectionException, ResponseParseException, IOException { // When serverFacade.makeRequest(method, path); - // Then InOrder inOrder = inOrder(mockedMockProvider); + + // Then inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("beforeAll"), any(), any()); this.verifyInOrder_authenticationMiddleware(path, inOrder); inOrder.verify(mockedMockProvider, times(1)).runHandler(eq(endpointName), any(), any()); From 076cec93a5a646607dd934bbfee2d016ab7d93d5 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 18:04:16 -0700 Subject: [PATCH 14/28] fix: remove duplicate test --- src/test/java/edu/byu/cs/server/ServerTest.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 597f0eb0..2d82f4ee 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -108,13 +108,6 @@ private void verifyInOrder_authenticationMiddleware(String path, InOrder inOrder } } - @Test - void nonexistent_GET_endpoint_calls_defaultGet_exactly_once() - throws IOException, ServerConnectionException, ResponseParseException { - serverFacade.makeRequest("GET", "/iDoNotExist"); - verify(mockedMockProvider, times(1)).runHandler(eq("defaultGet"), any(), any()); - } - @Test void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_exactly_once_in_order() throws IOException, ServerConnectionException, ResponseParseException { From 3cf5857a031091dff4c81226dfd688f050db77e3 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 18:27:27 -0700 Subject: [PATCH 15/28] refactor: remove unneeded parameters in runHandler() --- .../java/edu/byu/cs/server/ServerTest.java | 22 +++--- .../MockEndpointProvider.java | 76 +++++++++---------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 2d82f4ee..fab889ad 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -84,10 +84,10 @@ public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, Stri InOrder inOrder = inOrder(mockedMockProvider); // Then - inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("beforeAll"), any(), any()); + inOrder.verify(mockedMockProvider, times(1)).runHandler("beforeAll"); this.verifyInOrder_authenticationMiddleware(path, inOrder); - inOrder.verify(mockedMockProvider, times(1)).runHandler(eq(endpointName), any(), any()); - inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("afterAll"), any(), any()); + inOrder.verify(mockedMockProvider, times(1)).runHandler(endpointName); + inOrder.verify(mockedMockProvider, times(1)).runHandler("afterAll"); } private void verifyInOrder_authenticationMiddleware(String path, InOrder inOrder) { @@ -99,12 +99,12 @@ private void verifyInOrder_authenticationMiddleware(String path, InOrder inOrder if (!pathNodes.contains("auth")) { // Requires authentication - inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("verifyAuthenticatedMiddleware"), any(), any()); + inOrder.verify(mockedMockProvider, times(1)).runHandler("verifyAuthenticatedMiddleware"); } if (pathNodes.contains("admin")) { // Requires admin - inOrder.verify(mockedMockProvider, times(1)).runHandler(eq("verifyAdminMiddleware"), any(), any()); + inOrder.verify(mockedMockProvider, times(1)).runHandler("verifyAdminMiddleware"); } } @@ -115,13 +115,13 @@ void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_exac // Verify they ran in order InOrder inOrder = inOrder(mockedMockProvider); - inOrder.verify(mockedMockProvider).runHandler(eq("beforeAll"), any(), any()); - inOrder.verify(mockedMockProvider).runHandler(eq("defaultGet"), any(), any()); - inOrder.verify(mockedMockProvider).runHandler(eq("afterAll"), any(), any()); + inOrder.verify(mockedMockProvider).runHandler("beforeAll"); + inOrder.verify(mockedMockProvider).runHandler("defaultGet"); + inOrder.verify(mockedMockProvider).runHandler("afterAll"); // Verify they only ran once - verify(mockedMockProvider, times(1)).runHandler(eq("beforeAll"), any(), any()); - verify(mockedMockProvider, times(1)).runHandler(eq("defaultGet"), any(), any()); - verify(mockedMockProvider, times(1)).runHandler(eq("afterAll"), any(), any()); + verify(mockedMockProvider, times(1)).runHandler("beforeAll"); + verify(mockedMockProvider, times(1)).runHandler("defaultGet"); + verify(mockedMockProvider, times(1)).runHandler("afterAll"); } } \ No newline at end of file diff --git a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java index 3d986a13..cfdf6bf7 100644 --- a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java +++ b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java @@ -20,177 +20,177 @@ public class MockEndpointProvider implements EndpointProvider { * methods only run once on Server startup, so they can't be used * for that purpose. * - * @param name the name of the Route that was called - * @param req the Request object passed into the Route - * @param res the Response object passed into the Route - * @return null + * @param endpointName the name of the Route that was called */ - public Object runHandler(String name, Request req, Response res) { + public void runHandler(String endpointName) {} + + private Object extractRequestInfo(String endpointName, Request req, Response res) { + this.runHandler(endpointName); return "{}"; } @Override public Filter beforeAll() { - return (req, res) -> runHandler("beforeAll", req, res); + return (req, res) -> extractRequestInfo("beforeAll", req, res); } @Override public Filter afterAll() { - return (req, res) -> runHandler("afterAll", req, res); + return (req, res) -> extractRequestInfo("afterAll", req, res); } @Override public Route defaultGet() { - return (req, res) -> runHandler("defaultGet", req, res); + return (req, res) -> extractRequestInfo("defaultGet", req, res); } @Override public Route usersGet() { - return (req, res) -> runHandler("usersGet", req, res); + return (req, res) -> extractRequestInfo("usersGet", req, res); } @Override public Route testModeGet() { - return (req, res) -> runHandler("testModeGet", req, res); + return (req, res) -> extractRequestInfo("testModeGet", req, res); } @Override public Route commitAnalyticsGet() { - return (req, res) -> runHandler("commitAnalyticsGet", req, res); + return (req, res) -> extractRequestInfo("commitAnalyticsGet", req, res); } @Override public Route honorCheckerZipGet() { - return (req, res) -> runHandler("honorCheckerZipGet", req, res); + return (req, res) -> extractRequestInfo("honorCheckerZipGet", req, res); } @Override public Route sectionsGet() { - return (req, res) -> runHandler("sectionsGet", req, res); + return (req, res) -> extractRequestInfo("sectionsGet", req, res); } @Override public Filter verifyAuthenticatedMiddleware() { - return (req, res) -> runHandler("verifyAuthenticatedMiddleware", req, res); + return (req, res) -> extractRequestInfo("verifyAuthenticatedMiddleware", req, res); } @Override public Filter verifyAdminMiddleware() { - return (req, res) -> runHandler("verifyAdminMiddleware", req, res); + return (req, res) -> extractRequestInfo("verifyAdminMiddleware", req, res); } @Override public Route meGet() { - return (req, res) -> runHandler("meGet", req, res); + return (req, res) -> extractRequestInfo("meGet", req, res); } @Override public Route callbackGet() { - return (req, res) -> runHandler("callbackGet", req, res); + return (req, res) -> extractRequestInfo("callbackGet", req, res); } @Override public Route loginGet() { - return (req, res) -> runHandler("loginGet", req, res); + return (req, res) -> extractRequestInfo("loginGet", req, res); } @Override public Route logoutPost() { - return (req, res) -> runHandler("logoutPost", req, res); + return (req, res) -> extractRequestInfo("logoutPost", req, res); } @Override public Route getConfigAdmin() { - return (req, res) -> runHandler("getConfigAdmin", req, res); + return (req, res) -> extractRequestInfo("getConfigAdmin", req, res); } @Override public Route getConfigStudent() { - return (req, res) -> runHandler("getConfigStudent", req, res); + return (req, res) -> extractRequestInfo("getConfigStudent", req, res); } @Override public Route updateLivePhases() { - return (req, res) -> runHandler("updateLivePhases", req, res); + return (req, res) -> extractRequestInfo("updateLivePhases", req, res); } @Override public Route updateBannerMessage() { - return (req, res) -> runHandler("updateBannerMessage", req, res); + return (req, res) -> extractRequestInfo("updateBannerMessage", req, res); } @Override public Route updateCourseIdsPost() { - return (req, res) -> runHandler("updateCourseIdsPost", req, res); + return (req, res) -> extractRequestInfo("updateCourseIdsPost", req, res); } @Override public Route updateCourseIdsUsingCanvasGet() { - return (req, res) -> runHandler("updateCourseIdsUsingCanvasGet", req, res); + return (req, res) -> extractRequestInfo("updateCourseIdsUsingCanvasGet", req, res); } @Override public Route submitPost() { - return (req, res) -> runHandler("submitPost", req, res); + return (req, res) -> extractRequestInfo("submitPost", req, res); } @Override public Route adminRepoSubmitPost() { - return (req, res) -> runHandler("adminRepoSubmitPost", req, res); + return (req, res) -> extractRequestInfo("adminRepoSubmitPost", req, res); } @Override public Route submitGet() { - return (req, res) -> runHandler("submitGet", req, res); + return (req, res) -> extractRequestInfo("submitGet", req, res); } @Override public Route latestSubmissionForMeGet() { - return (req, res) -> runHandler("latestSubmissionForMeGet", req, res); + return (req, res) -> extractRequestInfo("latestSubmissionForMeGet", req, res); } @Override public Route submissionXGet() { - return (req, res) -> runHandler("submissionXGet", req, res); + return (req, res) -> extractRequestInfo("submissionXGet", req, res); } @Override public Route latestSubmissionsGet() { - return (req, res) -> runHandler("latestSubmissionsGet", req, res); + return (req, res) -> extractRequestInfo("latestSubmissionsGet", req, res); } @Override public Route submissionsActiveGet() { - return (req, res) -> runHandler("submissionsActiveGet", req, res); + return (req, res) -> extractRequestInfo("submissionsActiveGet", req, res); } @Override public Route studentSubmissionsGet() { - return (req, res) -> runHandler("studentSubmissionsGet", req, res); + return (req, res) -> extractRequestInfo("studentSubmissionsGet", req, res); } @Override public Route approveSubmissionPost() { - return (req, res) -> runHandler("approveSubmissionPost", req, res); + return (req, res) -> extractRequestInfo("approveSubmissionPost", req, res); } @Override public Route submissionsReRunPost() { - return (req, res) -> runHandler("submissionsReRunPost", req, res); + return (req, res) -> extractRequestInfo("submissionsReRunPost", req, res); } @Override public Route repoPatch() { - return (req, res) -> runHandler("repoPatch", req, res); + return (req, res) -> extractRequestInfo("repoPatch", req, res); } @Override public Route repoPatchAdmin() { - return (req, res) -> runHandler("repoPatchAdmin", req, res); + return (req, res) -> extractRequestInfo("repoPatchAdmin", req, res); } @Override public Route repoHistoryAdminGet() { - return (req, res) -> runHandler("repoHistoryAdminGet", req, res); + return (req, res) -> extractRequestInfo("repoHistoryAdminGet", req, res); } } From f091e48b91dc766f484369aa3276bc94df4c7215 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 18:29:16 -0700 Subject: [PATCH 16/28] feat: add capability of verifying path parameters to MockEndpointProvider --- .../MockEndpointProvider.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java index cfdf6bf7..14bbf07e 100644 --- a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java +++ b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java @@ -5,6 +5,8 @@ import spark.Response; import spark.Route; +import java.util.Map; + /** * A mock implementation of EndpointProvider designed for use with * Mockito.mock(). Because individual endpoint methods only run once at @@ -24,8 +26,26 @@ public class MockEndpointProvider implements EndpointProvider { */ public void runHandler(String endpointName) {} + /** + * An empty function that is run for each path parameter in each endpoint + * that is called. It is designed for use with Mockito.verify() to verify + * that endpoints are called with specific path parameters. + * + * @param endpointName the name of the Route that was called + * @param paramName the name of the parameter + * @param paramValue the value of the parameter + */ + public void hasPathParam(String endpointName, String paramName, String paramValue) {} + private Object extractRequestInfo(String endpointName, Request req, Response res) { + Map params = req.params(); + for (String paramName : params.keySet()) { + String paramValue = params.get(paramName); + this.hasPathParam(endpointName, paramName, paramValue); + } + this.runHandler(endpointName); + return "{}"; } From a39fbeb0d1b531174ca834159ab8d9fea85c132e Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 18:50:32 -0700 Subject: [PATCH 17/28] test: verify that pathParams are receiving values --- .../java/edu/byu/cs/server/ServerTest.java | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index fab889ad..f832f60b 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -21,14 +21,23 @@ class ServerTest { private static final MockEndpointProvider mockedMockProvider = spy(new MockEndpointProvider()); + public static Stream getPathParamEndpoints() { + return Stream.of( + Arguments.of("GET", "commitAnalyticsGet", "/api/admin/analytics/commit", ":option"), + Arguments.of("GET", "honorCheckerZipGet", "/api/admin/honorChecker/zip", ":section"), + Arguments.of("GET", "submissionXGet", "/api/submission", ":phase"), + Arguments.of("GET", "latestSubmissionsGet", "/api/admin/submissions/latest", ":count"), + Arguments.of("GET", "studentSubmissionsGet", "/api/admin/submissions/student", ":netid") + ); + } + public static Stream getEndpoints() { return Stream.of( Arguments.of("GET", "callbackGet", "/auth/callback"), Arguments.of("GET", "loginGet", "/auth/login"), Arguments.of("GET", "usersGet", "/api/admin/users"), Arguments.of("GET", "testModeGet", "/api/admin/test_mode"), - Arguments.of("GET", "commitAnalyticsGet", "/api/admin/analytics/commit"), // TODO: test with {option} -// Arguments.of("GET", "honorCheckerZipGet", "/api/admin/honorChecker/zip/{section}"), // TODO: requires {section} + Arguments.of("GET", "commitAnalyticsGet", "/api/admin/analytics/commit"), Arguments.of("GET", "sectionsGet", "/api/admin/sections"), Arguments.of("GET", "meGet", "/api/me"), Arguments.of("GET", "getConfigAdmin", "/api/admin/config"), @@ -36,10 +45,9 @@ public static Stream getEndpoints() { Arguments.of("GET", "updateCourseIdsUsingCanvasGet", "/api/admin/config/courseIds"), Arguments.of("GET", "submitGet", "/api/submit"), Arguments.of("GET", "latestSubmissionForMeGet", "/api/latest"), - Arguments.of("GET", "submissionXGet", "/api/submission"), // TODO test with {phase} - Arguments.of("GET", "latestSubmissionsGet", "/api/admin/submissions/latest"), // TODO: test with {count} + Arguments.of("GET", "submissionXGet", "/api/submission"), + Arguments.of("GET", "latestSubmissionsGet", "/api/admin/submissions/latest"), Arguments.of("GET", "submissionsActiveGet", "/api/admin/submissions/active"), -// Arguments.of("GET", "studentSubmissionsGet", "/admin/submissions/student/{netId}"), // TODO: requires {netId} Arguments.of("GET", "repoHistoryAdminGet", "/api/admin/repo/history"), Arguments.of("POST", "logoutPost", "/auth/logout"), Arguments.of("POST", "updateLivePhases", "/api/admin/config/phases"), @@ -53,7 +61,6 @@ public static Stream getEndpoints() { } // TODO figure out how to test PATCH calls... HttpURLConnection thinks it's an invalid method - // TODO verify endpoints that take pathParams @AfterAll static void stopServer() { @@ -90,6 +97,20 @@ public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, Stri inOrder.verify(mockedMockProvider, times(1)).runHandler("afterAll"); } + @ParameterizedTest + @MethodSource("getPathParamEndpoints") + public void verifyPathParameterHasAValueWhenGivenOne(String method, String endpointName, String path, String pathParamName) + throws ServerConnectionException, ResponseParseException, IOException { + // Given + String fullPath = path + "/testParamValue"; + + // When + serverFacade.makeRequest(method, fullPath); + + // Then + verify(mockedMockProvider, times(1)).hasPathParam(endpointName, pathParamName, "testParamValue"); + } + private void verifyInOrder_authenticationMiddleware(String path, InOrder inOrder) { List pathNodes = Arrays.stream(path.split("/")).toList(); From c89b8d435ff2fd624bc7a5662a457ab1fa5d44a4 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Sun, 17 Nov 2024 18:53:07 -0700 Subject: [PATCH 18/28] docs: clarify MockEndpointProvider should be used with spy(), not mock() --- .../byu/cs/server/endpointprovider/MockEndpointProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java index 14bbf07e..78ff8e81 100644 --- a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java +++ b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java @@ -9,7 +9,7 @@ /** * A mock implementation of EndpointProvider designed for use with - * Mockito.mock(). Because individual endpoint methods only run once at + * Mockito.spy() (not mock()). Because individual endpoint methods only run once at * Server startup, they aren't useful for verifying endpoint calls. * MockEndpointProvider provides a runHandler() method that can be used for * that purpose. From b37696cb11205ab5ac033a62b68d9dcc431d5c87 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Wed, 20 Nov 2024 14:51:53 -0700 Subject: [PATCH 19/28] fix: add scheduleShutdown to ServerTests --- src/test/java/edu/byu/cs/server/ServerTest.java | 1 + .../byu/cs/server/endpointprovider/MockEndpointProvider.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index f832f60b..00dd5c88 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -51,6 +51,7 @@ public static Stream getEndpoints() { Arguments.of("GET", "repoHistoryAdminGet", "/api/admin/repo/history"), Arguments.of("POST", "logoutPost", "/auth/logout"), Arguments.of("POST", "updateLivePhases", "/api/admin/config/phases"), + Arguments.of("POST", "scheduleShutdown", "/api/admin/config/phases/shutdown"), Arguments.of("POST", "updateBannerMessage", "/api/admin/config/banner"), Arguments.of("POST", "updateCourseIdsPost", "/api/admin/config/courseIds"), Arguments.of("POST", "submitPost", "/api/submit"), diff --git a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java index 78ff8e81..47d4c6dd 100644 --- a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java +++ b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java @@ -134,6 +134,11 @@ public Route updateLivePhases() { return (req, res) -> extractRequestInfo("updateLivePhases", req, res); } + @Override + public Route scheduleShutdown() { + return (req, res) -> extractRequestInfo("scheduleShutdown", req, res); + } + @Override public Route updateBannerMessage() { return (req, res) -> extractRequestInfo("updateBannerMessage", req, res); From d64061451ae7568b77c47455c3402f168feeb9c0 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Wed, 20 Nov 2024 15:00:32 -0700 Subject: [PATCH 20/28] enhance: rearrange arguments so path is before name first --- .../java/edu/byu/cs/server/ServerTest.java | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 00dd5c88..42997f46 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -23,41 +23,41 @@ class ServerTest { public static Stream getPathParamEndpoints() { return Stream.of( - Arguments.of("GET", "commitAnalyticsGet", "/api/admin/analytics/commit", ":option"), - Arguments.of("GET", "honorCheckerZipGet", "/api/admin/honorChecker/zip", ":section"), - Arguments.of("GET", "submissionXGet", "/api/submission", ":phase"), - Arguments.of("GET", "latestSubmissionsGet", "/api/admin/submissions/latest", ":count"), - Arguments.of("GET", "studentSubmissionsGet", "/api/admin/submissions/student", ":netid") + Arguments.of("GET", "/api/submission", "submissionXGet", ":phase"), + Arguments.of("GET", "/api/admin/analytics/commit", "commitAnalyticsGet", ":option"), + Arguments.of("GET", "/api/admin/honorChecker/zip", "honorCheckerZipGet", ":section"), + Arguments.of("GET", "/api/admin/submissions/latest", "latestSubmissionsGet", ":count"), + Arguments.of("GET", "/api/admin/submissions/student", "studentSubmissionsGet", ":netid") ); } public static Stream getEndpoints() { return Stream.of( - Arguments.of("GET", "callbackGet", "/auth/callback"), - Arguments.of("GET", "loginGet", "/auth/login"), - Arguments.of("GET", "usersGet", "/api/admin/users"), - Arguments.of("GET", "testModeGet", "/api/admin/test_mode"), - Arguments.of("GET", "commitAnalyticsGet", "/api/admin/analytics/commit"), - Arguments.of("GET", "sectionsGet", "/api/admin/sections"), - Arguments.of("GET", "meGet", "/api/me"), - Arguments.of("GET", "getConfigAdmin", "/api/admin/config"), - Arguments.of("GET", "getConfigStudent", "/api/config"), - Arguments.of("GET", "updateCourseIdsUsingCanvasGet", "/api/admin/config/courseIds"), - Arguments.of("GET", "submitGet", "/api/submit"), - Arguments.of("GET", "latestSubmissionForMeGet", "/api/latest"), - Arguments.of("GET", "submissionXGet", "/api/submission"), - Arguments.of("GET", "latestSubmissionsGet", "/api/admin/submissions/latest"), - Arguments.of("GET", "submissionsActiveGet", "/api/admin/submissions/active"), - Arguments.of("GET", "repoHistoryAdminGet", "/api/admin/repo/history"), - Arguments.of("POST", "logoutPost", "/auth/logout"), - Arguments.of("POST", "updateLivePhases", "/api/admin/config/phases"), - Arguments.of("POST", "scheduleShutdown", "/api/admin/config/phases/shutdown"), - Arguments.of("POST", "updateBannerMessage", "/api/admin/config/banner"), - Arguments.of("POST", "updateCourseIdsPost", "/api/admin/config/courseIds"), - Arguments.of("POST", "submitPost", "/api/submit"), - Arguments.of("POST", "adminRepoSubmitPost", "/api/admin/submit"), - Arguments.of("POST", "approveSubmissionPost", "/api/admin/submissions/approve"), - Arguments.of("POST", "submissionsReRunPost", "/api/admin/submissions/rerun") + Arguments.of("GET", "/auth/callback", "callbackGet"), + Arguments.of("GET", "/auth/login", "loginGet"), + Arguments.of("GET", "/api/admin/users", "usersGet"), + Arguments.of("GET", "/api/admin/test_mode", "testModeGet"), + Arguments.of("GET", "/api/admin/analytics/commit", "commitAnalyticsGet"), + Arguments.of("GET", "/api/admin/sections", "sectionsGet"), + Arguments.of("GET", "/api/me", "meGet"), + Arguments.of("GET", "/api/admin/config", "getConfigAdmin"), + Arguments.of("GET", "/api/config", "getConfigStudent"), + Arguments.of("GET", "/api/admin/config/courseIds", "updateCourseIdsUsingCanvasGet"), + Arguments.of("GET", "/api/submit", "submitGet"), + Arguments.of("GET", "/api/latest", "latestSubmissionForMeGet"), + Arguments.of("GET", "/api/submission", "submissionXGet"), + Arguments.of("GET", "/api/admin/submissions/latest", "latestSubmissionsGet"), + Arguments.of("GET", "/api/admin/submissions/active", "submissionsActiveGet"), + Arguments.of("GET", "/api/admin/repo/history", "repoHistoryAdminGet"), + Arguments.of("POST", "/auth/logout", "logoutPost"), + Arguments.of("POST", "/api/admin/config/phases", "updateLivePhases"), + Arguments.of("POST", "/api/admin/config/phases/shutdown", "scheduleShutdown"), + Arguments.of("POST", "/api/admin/config/banner", "updateBannerMessage"), + Arguments.of("POST", "/api/admin/config/courseIds", "updateCourseIdsPost"), + Arguments.of("POST", "/api/submit", "submitPost"), + Arguments.of("POST", "/api/admin/submit", "adminRepoSubmitPost"), + Arguments.of("POST", "/api/admin/submissions/approve", "approveSubmissionPost"), + Arguments.of("POST", "/api/admin/submissions/rerun", "submissionsReRunPost") ); } @@ -84,7 +84,7 @@ public void tearDown() { @ParameterizedTest @MethodSource("getEndpoints") - public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, String endpointName, String path) + public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, String path, String endpointName) throws ServerConnectionException, ResponseParseException, IOException { // When serverFacade.makeRequest(method, path); @@ -100,7 +100,7 @@ public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, Stri @ParameterizedTest @MethodSource("getPathParamEndpoints") - public void verifyPathParameterHasAValueWhenGivenOne(String method, String endpointName, String path, String pathParamName) + public void verifyPathParameterHasAValueWhenGivenOne(String method, String path, String endpointName, String pathParamName) throws ServerConnectionException, ResponseParseException, IOException { // Given String fullPath = path + "/testParamValue"; From 6b0521c19d9f7452c6045ca37f5b5774cbe4680f Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Wed, 20 Nov 2024 15:09:07 -0700 Subject: [PATCH 21/28] enhance: rearrange endpoint list using paths First by authorization (none/student/admin), then alphabetically by path. --- .../java/edu/byu/cs/server/ServerTest.java | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 42997f46..3871ac30 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -33,32 +33,36 @@ public static Stream getPathParamEndpoints() { public static Stream getEndpoints() { return Stream.of( - Arguments.of("GET", "/auth/callback", "callbackGet"), - Arguments.of("GET", "/auth/login", "loginGet"), - Arguments.of("GET", "/api/admin/users", "usersGet"), - Arguments.of("GET", "/api/admin/test_mode", "testModeGet"), - Arguments.of("GET", "/api/admin/analytics/commit", "commitAnalyticsGet"), - Arguments.of("GET", "/api/admin/sections", "sectionsGet"), - Arguments.of("GET", "/api/me", "meGet"), - Arguments.of("GET", "/api/admin/config", "getConfigAdmin"), - Arguments.of("GET", "/api/config", "getConfigStudent"), - Arguments.of("GET", "/api/admin/config/courseIds", "updateCourseIdsUsingCanvasGet"), - Arguments.of("GET", "/api/submit", "submitGet"), - Arguments.of("GET", "/api/latest", "latestSubmissionForMeGet"), - Arguments.of("GET", "/api/submission", "submissionXGet"), - Arguments.of("GET", "/api/admin/submissions/latest", "latestSubmissionsGet"), - Arguments.of("GET", "/api/admin/submissions/active", "submissionsActiveGet"), - Arguments.of("GET", "/api/admin/repo/history", "repoHistoryAdminGet"), + Arguments.of( "GET", "/auth/callback", "callbackGet"), + Arguments.of( "GET", "/auth/login", "loginGet"), Arguments.of("POST", "/auth/logout", "logoutPost"), - Arguments.of("POST", "/api/admin/config/phases", "updateLivePhases"), - Arguments.of("POST", "/api/admin/config/phases/shutdown", "scheduleShutdown"), + + Arguments.of( "GET", "/api/config", "getConfigStudent"), + Arguments.of( "GET", "/api/latest", "latestSubmissionForMeGet"), + Arguments.of( "GET", "/api/me", "meGet"), + Arguments.of( "GET", "/api/submission", "submissionXGet"), + Arguments.of( "GET", "/api/submit", "submitGet"), + Arguments.of("POST", "/api/submit", "submitPost"), + + Arguments.of( "GET", "/api/admin/config", "getConfigAdmin"), Arguments.of("POST", "/api/admin/config/banner", "updateBannerMessage"), Arguments.of("POST", "/api/admin/config/courseIds", "updateCourseIdsPost"), - Arguments.of("POST", "/api/submit", "submitPost"), - Arguments.of("POST", "/api/admin/submit", "adminRepoSubmitPost"), + Arguments.of( "GET", "/api/admin/config/courseIds", "updateCourseIdsUsingCanvasGet"), + Arguments.of("POST", "/api/admin/config/phases", "updateLivePhases"), + Arguments.of("POST", "/api/admin/config/phases/shutdown", "scheduleShutdown"), + + Arguments.of( "GET", "/api/admin/submissions/active", "submissionsActiveGet"), Arguments.of("POST", "/api/admin/submissions/approve", "approveSubmissionPost"), - Arguments.of("POST", "/api/admin/submissions/rerun", "submissionsReRunPost") - ); + Arguments.of( "GET", "/api/admin/submissions/latest", "latestSubmissionsGet"), + Arguments.of("POST", "/api/admin/submissions/rerun", "submissionsReRunPost"), + Arguments.of("POST", "/api/admin/submit", "adminRepoSubmitPost"), + + Arguments.of( "GET", "/api/admin/analytics/commit", "commitAnalyticsGet"), + Arguments.of( "GET", "/api/admin/repo/history", "repoHistoryAdminGet"), + Arguments.of( "GET", "/api/admin/sections", "sectionsGet"), + Arguments.of( "GET", "/api/admin/test_mode", "testModeGet"), + Arguments.of( "GET", "/api/admin/users", "usersGet") + ); } // TODO figure out how to test PATCH calls... HttpURLConnection thinks it's an invalid method From 9ff3cdfcec70426b7e1501bd8a14efea1652b16d Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Wed, 20 Nov 2024 15:15:53 -0700 Subject: [PATCH 22/28] fix: replace HTTP PATCH requests with POST Our TestServerFacade thinks PATCH is invalid, and it's arguably kinda pointless anyway. --- .../java/edu/byu/cs/controller/UserController.java | 10 +++++----- src/main/java/edu/byu/cs/server/Server.java | 4 ++-- .../cs/server/endpointprovider/EndpointProvider.java | 4 ++-- .../server/endpointprovider/EndpointProviderImpl.java | 10 +++++----- .../server/endpointprovider/MockEndpointProvider.java | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/edu/byu/cs/controller/UserController.java b/src/main/java/edu/byu/cs/controller/UserController.java index 48e8a7c6..e03f2c77 100644 --- a/src/main/java/edu/byu/cs/controller/UserController.java +++ b/src/main/java/edu/byu/cs/controller/UserController.java @@ -18,16 +18,16 @@ import static spark.Spark.halt; public class UserController { - public static final Route repoPatch = (req, res) -> { + public static final Route setRepoUrl = (req, res) -> { User user = req.session().attribute("user"); - applyRepoPatch(user.netId(), null, req, res); + setRepoUrl(user.netId(), null, req, res); return "Successfully updated repoUrl"; }; - public static final Route repoPatchAdmin = (req, res) -> { + public static final Route setRepoUrlAdmin = (req, res) -> { User admin = req.session().attribute("user"); String studentNetId = req.params(":netId"); - applyRepoPatch(studentNetId, admin.netId(), req, res); + setRepoUrl(studentNetId, admin.netId(), req, res); return "Successfully updated repoUrl for user: " + studentNetId; }; @@ -52,7 +52,7 @@ public class UserController { return Serializer.serialize(updates); }; - private static void applyRepoPatch(String studentNetId, String adminNetId, Request req, Response res) { + private static void setRepoUrl(String studentNetId, String adminNetId, Request req, Response res) { JsonObject jsonObject = new Gson().fromJson(req.body(), JsonObject.class); String repoUrl = new Gson().fromJson(jsonObject.get("repoUrl"), String.class); diff --git a/src/main/java/edu/byu/cs/server/Server.java b/src/main/java/edu/byu/cs/server/Server.java index 41c1d176..259e3ca4 100644 --- a/src/main/java/edu/byu/cs/server/Server.java +++ b/src/main/java/edu/byu/cs/server/Server.java @@ -51,7 +51,7 @@ private int setupEndpoints(int port) { if (!req.requestMethod().equals("OPTIONS")) provider.verifyAuthenticatedMiddleware().handle(req, res); }); - patch("/repo", provider.repoPatch()); + post("/repo", provider.setRepoUrl()); get("/submit", provider.submitGet()); post("/submit", provider.submitPost()); @@ -70,7 +70,7 @@ private int setupEndpoints(int port) { if (!req.requestMethod().equals("OPTIONS")) provider.verifyAdminMiddleware().handle(req, res); }); - patch("/repo/:netId", provider.repoPatchAdmin()); + post("/repo/:netId", provider.setRepoUrlAdmin()); get("/repo/history", provider.repoHistoryAdminGet()); diff --git a/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java index 0ef17ef1..f9e4a08a 100644 --- a/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java +++ b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProvider.java @@ -57,7 +57,7 @@ public interface EndpointProvider { // UserController - Route repoPatch(); - Route repoPatchAdmin(); + Route setRepoUrl(); + Route setRepoUrlAdmin(); Route repoHistoryAdminGet(); } diff --git a/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java index 1e4584ee..504173ab 100644 --- a/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java +++ b/src/main/java/edu/byu/cs/server/endpointprovider/EndpointProviderImpl.java @@ -13,7 +13,7 @@ public class EndpointProviderImpl implements EndpointProvider { public Filter beforeAll() { return (request, response) -> { response.header("Access-Control-Allow-Headers", "Authorization,Content-Type"); - response.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,PATCH,OPTIONS"); + response.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS"); response.header("Access-Control-Allow-Credentials", "true"); response.header("Access-Control-Allow-Origin", ApplicationProperties.frontendUrl()); }; @@ -190,13 +190,13 @@ public Route submissionsReRunPost() { // UserController @Override - public Route repoPatch() { - return UserController.repoPatch; + public Route setRepoUrl() { + return UserController.setRepoUrl; } @Override - public Route repoPatchAdmin() { - return UserController.repoPatchAdmin; + public Route setRepoUrlAdmin() { + return UserController.setRepoUrlAdmin; } @Override diff --git a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java index 47d4c6dd..558d337d 100644 --- a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java +++ b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java @@ -205,13 +205,13 @@ public Route submissionsReRunPost() { } @Override - public Route repoPatch() { - return (req, res) -> extractRequestInfo("repoPatch", req, res); + public Route setRepoUrl() { + return (req, res) -> extractRequestInfo("setRepoUrl", req, res); } @Override - public Route repoPatchAdmin() { - return (req, res) -> extractRequestInfo("repoPatchAdmin", req, res); + public Route setRepoUrlAdmin() { + return (req, res) -> extractRequestInfo("setRepoUrlAdmin", req, res); } @Override From b93d60087f0b7f5713c71bf1f8c34c0f936147a6 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Wed, 20 Nov 2024 15:17:09 -0700 Subject: [PATCH 23/28] add no-longer-patch requests to ServerTest --- src/test/java/edu/byu/cs/server/ServerTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 3871ac30..15bbfc0d 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -23,11 +23,12 @@ class ServerTest { public static Stream getPathParamEndpoints() { return Stream.of( - Arguments.of("GET", "/api/submission", "submissionXGet", ":phase"), - Arguments.of("GET", "/api/admin/analytics/commit", "commitAnalyticsGet", ":option"), - Arguments.of("GET", "/api/admin/honorChecker/zip", "honorCheckerZipGet", ":section"), - Arguments.of("GET", "/api/admin/submissions/latest", "latestSubmissionsGet", ":count"), - Arguments.of("GET", "/api/admin/submissions/student", "studentSubmissionsGet", ":netid") + Arguments.of( "GET", "/api/submission", "submissionXGet", ":phase"), + Arguments.of( "GET", "/api/admin/analytics/commit", "commitAnalyticsGet", ":option"), + Arguments.of("POST", "/api/admin/repo", "setRepoUrlAdmin", ":netid"), + Arguments.of( "GET", "/api/admin/honorChecker/zip", "honorCheckerZipGet", ":section"), + Arguments.of( "GET", "/api/admin/submissions/latest", "latestSubmissionsGet", ":count"), + Arguments.of( "GET", "/api/admin/submissions/student", "studentSubmissionsGet", ":netid") ); } @@ -40,6 +41,7 @@ public static Stream getEndpoints() { Arguments.of( "GET", "/api/config", "getConfigStudent"), Arguments.of( "GET", "/api/latest", "latestSubmissionForMeGet"), Arguments.of( "GET", "/api/me", "meGet"), + Arguments.of("POST", "/api/repo", "setRepoUrl"), Arguments.of( "GET", "/api/submission", "submissionXGet"), Arguments.of( "GET", "/api/submit", "submitGet"), Arguments.of("POST", "/api/submit", "submitPost"), @@ -65,8 +67,6 @@ public static Stream getEndpoints() { ); } - // TODO figure out how to test PATCH calls... HttpURLConnection thinks it's an invalid method - @AfterAll static void stopServer() { server.stop(); From a7276a9051dbcca36ad329d15d4b888f45a5a00d Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Wed, 20 Nov 2024 15:17:09 -0700 Subject: [PATCH 24/28] add no-longer-patch requests to ServerTest --- .../java/edu/byu/cs/server/ServerTest.java | 26 +++++++++---------- .../edu/byu/cs/server/TestServerFacade.java | 3 ++- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 3871ac30..5ee834a3 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -23,11 +23,12 @@ class ServerTest { public static Stream getPathParamEndpoints() { return Stream.of( - Arguments.of("GET", "/api/submission", "submissionXGet", ":phase"), - Arguments.of("GET", "/api/admin/analytics/commit", "commitAnalyticsGet", ":option"), - Arguments.of("GET", "/api/admin/honorChecker/zip", "honorCheckerZipGet", ":section"), - Arguments.of("GET", "/api/admin/submissions/latest", "latestSubmissionsGet", ":count"), - Arguments.of("GET", "/api/admin/submissions/student", "studentSubmissionsGet", ":netid") + Arguments.of( "GET", "/api/submission", "submissionXGet", ":phase"), + Arguments.of( "GET", "/api/admin/analytics/commit", "commitAnalyticsGet", ":option"), + Arguments.of("POST", "/api/admin/repo", "setRepoUrlAdmin", ":netid"), + Arguments.of( "GET", "/api/admin/honorChecker/zip", "honorCheckerZipGet", ":section"), + Arguments.of( "GET", "/api/admin/submissions/latest", "latestSubmissionsGet", ":count"), + Arguments.of( "GET", "/api/admin/submissions/student", "studentSubmissionsGet", ":netid") ); } @@ -40,6 +41,7 @@ public static Stream getEndpoints() { Arguments.of( "GET", "/api/config", "getConfigStudent"), Arguments.of( "GET", "/api/latest", "latestSubmissionForMeGet"), Arguments.of( "GET", "/api/me", "meGet"), + Arguments.of("POST", "/api/repo", "setRepoUrl"), Arguments.of( "GET", "/api/submission", "submissionXGet"), Arguments.of( "GET", "/api/submit", "submitGet"), Arguments.of("POST", "/api/submit", "submitPost"), @@ -65,8 +67,6 @@ public static Stream getEndpoints() { ); } - // TODO figure out how to test PATCH calls... HttpURLConnection thinks it's an invalid method - @AfterAll static void stopServer() { server.stop(); @@ -88,8 +88,7 @@ public void tearDown() { @ParameterizedTest @MethodSource("getEndpoints") - public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, String path, String endpointName) - throws ServerConnectionException, ResponseParseException, IOException { + public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, String path, String endpointName) throws ServerConnectionException, ResponseParseException, IOException { // When serverFacade.makeRequest(method, path); @@ -104,8 +103,8 @@ public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, Stri @ParameterizedTest @MethodSource("getPathParamEndpoints") - public void verifyPathParameterHasAValueWhenGivenOne(String method, String path, String endpointName, String pathParamName) - throws ServerConnectionException, ResponseParseException, IOException { + public void verifyPathParameterHasAValueWhenGivenOne(String method, String path, String endpointName, + String pathParamName) throws ServerConnectionException, ResponseParseException, IOException { // Given String fullPath = path + "/testParamValue"; @@ -135,8 +134,7 @@ private void verifyInOrder_authenticationMiddleware(String path, InOrder inOrder } @Test - void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_exactly_once_in_order() - throws IOException, ServerConnectionException, ResponseParseException { + void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_exactly_once_in_order() throws IOException, ServerConnectionException, ResponseParseException { serverFacade.makeRequest("GET", "/iDoNotExist"); // Verify they ran in order @@ -150,4 +148,4 @@ void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_exac verify(mockedMockProvider, times(1)).runHandler("defaultGet"); verify(mockedMockProvider, times(1)).runHandler("afterAll"); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/byu/cs/server/TestServerFacade.java b/src/test/java/edu/byu/cs/server/TestServerFacade.java index 5c83ddfd..19099383 100644 --- a/src/test/java/edu/byu/cs/server/TestServerFacade.java +++ b/src/test/java/edu/byu/cs/server/TestServerFacade.java @@ -21,7 +21,8 @@ public TestServerFacade(String serverURL, int port) { this.serverURL = "http://%s:%d".formatted(serverURL, port); } - public Object makeRequest(String method, String path) throws IOException, ServerConnectionException, ResponseParseException { + public Object makeRequest(String method, String path) throws IOException, ServerConnectionException, + ResponseParseException { return makeRequest(method, path, null, null, Object.class); } From 46d9eee2785a4be4a7b949fee8ac89da6f47f890 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Wed, 20 Nov 2024 23:56:15 -0700 Subject: [PATCH 25/28] enhance: un-hardcode Server ports --- src/main/java/Main.java | 2 +- src/main/java/edu/byu/cs/server/Server.java | 4 ++++ src/test/java/edu/byu/cs/server/ServerTest.java | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/Main.java b/src/main/java/Main.java index a533f101..41a88ac5 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -31,7 +31,7 @@ public static void main(String[] args) { throw new RuntimeException(e); } - new Server(endpointProvider).start(8080); + new Server(endpointProvider).start(); try { SubmissionService.reRunSubmissionsInQueue(); diff --git a/src/main/java/edu/byu/cs/server/Server.java b/src/main/java/edu/byu/cs/server/Server.java index 259e3ca4..52646801 100644 --- a/src/main/java/edu/byu/cs/server/Server.java +++ b/src/main/java/edu/byu/cs/server/Server.java @@ -17,6 +17,10 @@ public Server(EndpointProvider endpointProvider) { this.provider = endpointProvider; } + public int start() { + return start(0); + } + public int start(int desiredPort) { int chosenPort = setupEndpoints(desiredPort); LOGGER.info("Server started on port {}", chosenPort); diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 5ee834a3..83fb389c 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -75,7 +75,7 @@ static void stopServer() { @BeforeAll public static void init() { server = new Server(mockedMockProvider); - int port = server.start(8080); + int port = server.start(); System.out.println("Started test HTTP server on " + port); serverFacade = new TestServerFacade("localhost", port); From 6886c1ba9edcec2505d68eb96543475f0d37cff5 Mon Sep 17 00:00:00 2001 From: Nathaniel Gerlek Date: Thu, 21 Nov 2024 00:07:50 -0700 Subject: [PATCH 26/28] test: rmv custom ServerFacadeTest exceptions --- .../java/edu/byu/cs/server/ServerTest.java | 8 +++---- .../edu/byu/cs/server/TestServerFacade.java | 24 ++++++++----------- .../exception/ResponseParseException.java | 7 ------ .../exception/ServerConnectionException.java | 7 ------ 4 files changed, 13 insertions(+), 33 deletions(-) delete mode 100644 src/test/java/edu/byu/cs/server/exception/ResponseParseException.java delete mode 100644 src/test/java/edu/byu/cs/server/exception/ServerConnectionException.java diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 83fb389c..23b839fb 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -1,8 +1,6 @@ package edu.byu.cs.server; import edu.byu.cs.server.endpointprovider.MockEndpointProvider; -import edu.byu.cs.server.exception.ResponseParseException; -import edu.byu.cs.server.exception.ServerConnectionException; import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -88,7 +86,7 @@ public void tearDown() { @ParameterizedTest @MethodSource("getEndpoints") - public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, String path, String endpointName) throws ServerConnectionException, ResponseParseException, IOException { + public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, String path, String endpointName) throws IOException { // When serverFacade.makeRequest(method, path); @@ -104,7 +102,7 @@ public void verifyEndpointCallsItsHandlersExactlyOnceInOrder(String method, Stri @ParameterizedTest @MethodSource("getPathParamEndpoints") public void verifyPathParameterHasAValueWhenGivenOne(String method, String path, String endpointName, - String pathParamName) throws ServerConnectionException, ResponseParseException, IOException { + String pathParamName) throws IOException { // Given String fullPath = path + "/testParamValue"; @@ -134,7 +132,7 @@ private void verifyInOrder_authenticationMiddleware(String path, InOrder inOrder } @Test - void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_exactly_once_in_order() throws IOException, ServerConnectionException, ResponseParseException { + void nonexistent_GET_endpoint_calls_beforeAll_then_defaultGet_then_afterAll_exactly_once_in_order() throws IOException { serverFacade.makeRequest("GET", "/iDoNotExist"); // Verify they ran in order diff --git a/src/test/java/edu/byu/cs/server/TestServerFacade.java b/src/test/java/edu/byu/cs/server/TestServerFacade.java index 19099383..43ffc4ce 100644 --- a/src/test/java/edu/byu/cs/server/TestServerFacade.java +++ b/src/test/java/edu/byu/cs/server/TestServerFacade.java @@ -1,8 +1,6 @@ package edu.byu.cs.server; import com.google.gson.Gson; -import edu.byu.cs.server.exception.ResponseParseException; -import edu.byu.cs.server.exception.ServerConnectionException; import java.io.IOException; import java.io.InputStream; @@ -21,20 +19,19 @@ public TestServerFacade(String serverURL, int port) { this.serverURL = "http://%s:%d".formatted(serverURL, port); } - public Object makeRequest(String method, String path) throws IOException, ServerConnectionException, - ResponseParseException { + public Object makeRequest(String method, String path) throws IOException { return makeRequest(method, path, null, null, Object.class); } public T makeRequest(String method, String path, Object request, Map headers, - Class responseClass) throws IOException, ServerConnectionException, ResponseParseException { + Class responseClass) throws IOException { HttpURLConnection http = getConnection(method, serverURL + path); writeRequest(http, request, headers); connect(http); return readResponse(http, responseClass); } - private HttpURLConnection getConnection(String method, String urlString) throws ServerConnectionException { + private HttpURLConnection getConnection(String method, String urlString) throws IOException { try { URL url = (new URI(urlString)).toURL(); HttpURLConnection http = (HttpURLConnection) url.openConnection(); @@ -42,35 +39,34 @@ private HttpURLConnection getConnection(String method, String urlString) throws http.setDoOutput("POST".equals(method) || "PUT".equals(method)); return http; } catch (IOException | URISyntaxException e) { - throw new ServerConnectionException("Failed to set up HTTP connection: " + e.getMessage()); + throw new IOException("Failed to set up HTTP connection: " + e.getMessage(), e); } } - private void writeRequest(HttpURLConnection http, Object requestBody, Map headers) throws ServerConnectionException { + private void writeRequest(HttpURLConnection http, Object requestBody, Map headers) throws IOException { try { writeHeaders(http, headers); writeRequestBody(http, requestBody); } catch (IOException e) { - throw new ServerConnectionException("Could not write request body: " + e.getMessage()); + throw new IOException("Could not write request body: " + e.getMessage(), e); } } - private void connect(HttpURLConnection http) throws ServerConnectionException { + private void connect(HttpURLConnection http) throws IOException { try { http.connect(); } catch (IOException e) { - throw new ServerConnectionException("Failed to connect to server: " + e.getMessage()); + throw new IOException("Failed to connect to server: " + e.getMessage(), e); } } - private T readResponse(HttpURLConnection http, Class responseClass) throws IOException, - ResponseParseException { + private T readResponse(HttpURLConnection http, Class responseClass) throws IOException { String respString = getRespString(http); try { return new Gson().fromJson(respString, responseClass); } catch (Exception e) { String message = String.format("Error parsing response. Expected JSON, got '%s'", respString); - throw new ResponseParseException(message, e); + throw new IOException(message, e); } } diff --git a/src/test/java/edu/byu/cs/server/exception/ResponseParseException.java b/src/test/java/edu/byu/cs/server/exception/ResponseParseException.java deleted file mode 100644 index 366ab2ee..00000000 --- a/src/test/java/edu/byu/cs/server/exception/ResponseParseException.java +++ /dev/null @@ -1,7 +0,0 @@ -package edu.byu.cs.server.exception; - -public class ResponseParseException extends Exception { - public ResponseParseException(String message, Exception cause) { - super(message, cause); - } -} diff --git a/src/test/java/edu/byu/cs/server/exception/ServerConnectionException.java b/src/test/java/edu/byu/cs/server/exception/ServerConnectionException.java deleted file mode 100644 index 7340f4cb..00000000 --- a/src/test/java/edu/byu/cs/server/exception/ServerConnectionException.java +++ /dev/null @@ -1,7 +0,0 @@ -package edu.byu.cs.server.exception; - -public class ServerConnectionException extends Exception { - public ServerConnectionException(String message) { - super(message); - } -} From 4999c4e7ed48b966e2566092c9bee8cc74630b08 Mon Sep 17 00:00:00 2001 From: ThanGerlek Date: Mon, 25 Nov 2024 18:17:44 +0000 Subject: [PATCH 27/28] fix: add updatePenalties() to ServerTest --- src/test/java/edu/byu/cs/server/ServerTest.java | 2 ++ .../byu/cs/server/endpointprovider/MockEndpointProvider.java | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/test/java/edu/byu/cs/server/ServerTest.java b/src/test/java/edu/byu/cs/server/ServerTest.java index 23b839fb..9437fac4 100644 --- a/src/test/java/edu/byu/cs/server/ServerTest.java +++ b/src/test/java/edu/byu/cs/server/ServerTest.java @@ -28,6 +28,7 @@ public static Stream getPathParamEndpoints() { Arguments.of( "GET", "/api/admin/submissions/latest", "latestSubmissionsGet", ":count"), Arguments.of( "GET", "/api/admin/submissions/student", "studentSubmissionsGet", ":netid") ); + // api/admin/config/penalties } public static Stream getEndpoints() { @@ -48,6 +49,7 @@ public static Stream getEndpoints() { Arguments.of("POST", "/api/admin/config/banner", "updateBannerMessage"), Arguments.of("POST", "/api/admin/config/courseIds", "updateCourseIdsPost"), Arguments.of( "GET", "/api/admin/config/courseIds", "updateCourseIdsUsingCanvasGet"), + Arguments.of("POST", "/api/admin/config/penalties", "updatePenalties"), Arguments.of("POST", "/api/admin/config/phases", "updateLivePhases"), Arguments.of("POST", "/api/admin/config/phases/shutdown", "scheduleShutdown"), diff --git a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java index 558d337d..1b977b9c 100644 --- a/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java +++ b/src/test/java/edu/byu/cs/server/endpointprovider/MockEndpointProvider.java @@ -154,6 +154,11 @@ public Route updateCourseIdsUsingCanvasGet() { return (req, res) -> extractRequestInfo("updateCourseIdsUsingCanvasGet", req, res); } + @Override + public Route updatePenalties() { + return (req, res) -> extractRequestInfo("updatePenalties", req, res); + } + @Override public Route submitPost() { return (req, res) -> extractRequestInfo("submitPost", req, res); From 26aa6e38298910e0d9ba811e8923e3d625edb672 Mon Sep 17 00:00:00 2001 From: ThanGerlek Date: Mon, 25 Nov 2024 18:25:23 +0000 Subject: [PATCH 28/28] fix: call awaitInitialization() before port() --- src/main/java/edu/byu/cs/server/Server.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/byu/cs/server/Server.java b/src/main/java/edu/byu/cs/server/Server.java index b89a5d82..7ee46988 100644 --- a/src/main/java/edu/byu/cs/server/Server.java +++ b/src/main/java/edu/byu/cs/server/Server.java @@ -24,7 +24,6 @@ public int start() { public int start(int desiredPort) { int chosenPort = setupEndpoints(desiredPort); LOGGER.info("Server started on port {}", chosenPort); - awaitInitialization(); return chosenPort; } @@ -128,6 +127,8 @@ private int setupEndpoints(int port) { init(); + awaitInitialization(); + return port(); } }