From efd302f625e37a39139ecf699b2e6620cfeaafa4 Mon Sep 17 00:00:00 2001 From: CodingPhoenixx Date: Thu, 28 May 2026 13:48:05 +0200 Subject: [PATCH] Expand test coverage for routing and annotation scanning: validate distinct handlers for same path with different methods, ensure correct MethodNotAllowed responses, and handle overwriting/parameterized paths. --- .../annotation/AnnotationScannerTest.java | 41 ++++++++++ .../nextusweb/server/router/RouterTest.java | 80 +++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/src/test/java/dev/coph/nextusweb/server/annotation/AnnotationScannerTest.java b/src/test/java/dev/coph/nextusweb/server/annotation/AnnotationScannerTest.java index c26b699..708b6f4 100644 --- a/src/test/java/dev/coph/nextusweb/server/annotation/AnnotationScannerTest.java +++ b/src/test/java/dev/coph/nextusweb/server/annotation/AnnotationScannerTest.java @@ -110,4 +110,45 @@ class AnnotationScannerTest { assertThrows(IllegalArgumentException.class, () -> AnnotationScanner.register(router, new WrongReturnType())); } + + @Controller("/users") + static class CrudController { + java.util.concurrent.atomic.AtomicInteger getCalls = new java.util.concurrent.atomic.AtomicInteger(); + java.util.concurrent.atomic.AtomicInteger putCalls = new java.util.concurrent.atomic.AtomicInteger(); + java.util.concurrent.atomic.AtomicInteger deleteCalls = new java.util.concurrent.atomic.AtomicInteger(); + + @GET("/") + public void list(Request req, Response res) { getCalls.incrementAndGet(); } + + @PUT("/") + public void replace(Request req, Response res) { putCalls.incrementAndGet(); } + + @DELETE("/") + public void wipe(Request req, Response res) { deleteCalls.incrementAndGet(); } + } + + @Test + void multipleMethodsOnSamePathRouteToDistinctHandlers() throws Exception { + Router router = new Router(); + CrudController ctrl = new CrudController(); + AnnotationScanner.register(router, ctrl); + + var get = assertInstanceOf(Router.Resolution.Match.class, router.resolve(HttpMethod.GET, "/users/")); + var put = assertInstanceOf(Router.Resolution.Match.class, router.resolve(HttpMethod.PUT, "/users/")); + var del = assertInstanceOf(Router.Resolution.Match.class, router.resolve(HttpMethod.DELETE, "/users/")); + + get.handler().handle(null, new Response()); + put.handler().handle(null, new Response()); + del.handler().handle(null, new Response()); + + assertEquals(1, ctrl.getCalls.get()); + assertEquals(1, ctrl.putCalls.get()); + assertEquals(1, ctrl.deleteCalls.get()); + + var post = router.resolve(HttpMethod.POST, "/users/"); + var mna = assertInstanceOf(Router.Resolution.MethodNotAllowed.class, post); + assertTrue(mna.allowedMethods().contains(HttpMethod.GET)); + assertTrue(mna.allowedMethods().contains(HttpMethod.PUT)); + assertTrue(mna.allowedMethods().contains(HttpMethod.DELETE)); + } } diff --git a/src/test/java/dev/coph/nextusweb/server/router/RouterTest.java b/src/test/java/dev/coph/nextusweb/server/router/RouterTest.java index 3c26071..84d1477 100644 --- a/src/test/java/dev/coph/nextusweb/server/router/RouterTest.java +++ b/src/test/java/dev/coph/nextusweb/server/router/RouterTest.java @@ -80,4 +80,84 @@ class RouterTest { match.handler().handle(null, null); assertEquals(1, called.get()); } + + @Test + void samePathWithDifferentMethodsResolvesToDistinctHandlers() throws Exception { + AtomicInteger getCalls = new AtomicInteger(); + AtomicInteger putCalls = new AtomicInteger(); + AtomicInteger deleteCalls = new AtomicInteger(); + + Router r = new Router() + .get("/user", (req, res) -> getCalls.incrementAndGet()) + .put("/user", (req, res) -> putCalls.incrementAndGet()) + .delete("/user", (req, res) -> deleteCalls.incrementAndGet()); + + var get = assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.GET, "/user")); + var put = assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.PUT, "/user")); + var del = assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.DELETE, "/user")); + + get.handler().handle(null, null); + put.handler().handle(null, null); + del.handler().handle(null, null); + + assertEquals(1, getCalls.get()); + assertEquals(1, putCalls.get()); + assertEquals(1, deleteCalls.get()); + assertNotSame(get.handler(), put.handler()); + assertNotSame(put.handler(), del.handler()); + } + + @Test + void samePathUnregisteredMethodReturnsMethodNotAllowedWithAllAllowed() { + Router r = new Router() + .get("/user", noop) + .put("/user", noop) + .delete("/user", noop); + + var res = r.resolve(HttpMethod.POST, "/user"); + var mna = assertInstanceOf(Router.Resolution.MethodNotAllowed.class, res); + assertTrue(mna.allowedMethods().contains(HttpMethod.GET)); + assertTrue(mna.allowedMethods().contains(HttpMethod.PUT)); + assertTrue(mna.allowedMethods().contains(HttpMethod.DELETE)); + assertFalse(mna.allowedMethods().contains(HttpMethod.POST)); + assertEquals(3, mna.allowedMethods().size()); + } + + @Test + void registeringSameMethodAndPathTwiceOverwritesHandler() throws Exception { + AtomicInteger first = new AtomicInteger(); + AtomicInteger second = new AtomicInteger(); + + Router r = new Router() + .get("/user", (req, res) -> first.incrementAndGet()) + .get("/user", (req, res) -> second.incrementAndGet()); + + var match = (Router.Resolution.Match) r.resolve(HttpMethod.GET, "/user"); + match.handler().handle(null, null); + + assertEquals(0, first.get()); + assertEquals(1, second.get()); + } + + @Test + void samePathWithParamAndMultipleMethodsKeepsParamsAndHandlers() throws Exception { + AtomicInteger getCalls = new AtomicInteger(); + AtomicInteger putCalls = new AtomicInteger(); + + Router r = new Router() + .get("/user/{id}", (req, res) -> getCalls.incrementAndGet()) + .put("/user/{id}", (req, res) -> putCalls.incrementAndGet()); + + var get = assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.GET, "/user/42")); + var put = assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.PUT, "/user/42")); + + assertEquals("42", get.pathParams().get("id")); + assertEquals("42", put.pathParams().get("id")); + + get.handler().handle(null, null); + put.handler().handle(null, null); + assertEquals(1, getCalls.get()); + assertEquals(1, putCalls.get()); + assertNotSame(get.handler(), put.handler()); + } }