package dev.coph.nextusweb.server.router; import io.netty.handler.codec.http.HttpMethod; import org.junit.jupiter.api.Test; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.*; class RouterTest { private final Router.Handler noop = (req, res) -> {}; @Test void getRegistersAndResolvesExactPath() { Router r = new Router().get("/hello", noop); assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.GET, "/hello")); } @Test void postPutDeleteRegister() { Router r = new Router() .post("/p", noop) .put("/u", noop) .delete("/d", noop); assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.POST, "/p")); assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.PUT, "/u")); assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.DELETE, "/d")); } @Test void notFoundForUnknownPath() { Router r = new Router().get("/a", noop); assertInstanceOf(Router.Resolution.NotFound.class, r.resolve(HttpMethod.GET, "/x")); } @Test void methodNotAllowedWhenPathMatchesDifferentMethod() { Router r = new Router().get("/a", noop); Router.Resolution res = r.resolve(HttpMethod.POST, "/a"); Router.Resolution.MethodNotAllowed mna = assertInstanceOf(Router.Resolution.MethodNotAllowed.class, res); assertTrue(mna.allowedMethods().contains(HttpMethod.GET)); } @Test void pathParamsAreExtracted() { Router r = new Router().get("/u/{id}", noop); Router.Resolution res = r.resolve(HttpMethod.GET, "/u/42"); Router.Resolution.Match m = assertInstanceOf(Router.Resolution.Match.class, res); assertEquals("42", m.pathParams().get("id")); } @Test void wildcardMatches() { Router r = new Router().get("/files/*", noop); assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.GET, "/files/anything")); } @Test void useAddsMiddlewareReturned() { AtomicInteger count = new AtomicInteger(); Router r = new Router().use((req, res) -> count.incrementAndGet()); assertEquals(1, r.middlewares().size()); r.middlewares().getFirst().accept(null, null); assertEquals(1, count.get()); } @Test void registerWorksWithCustomMethod() { Router r = new Router().register(HttpMethod.valueOf("OPTIONS"), "/x", noop); assertInstanceOf(Router.Resolution.Match.class, r.resolve(HttpMethod.valueOf("OPTIONS"), "/x")); } @Test void handlerInvocationWorks() throws Exception { AtomicInteger called = new AtomicInteger(); Router r = new Router().get("/x", (req, res) -> called.incrementAndGet()); var match = (Router.Resolution.Match) r.resolve(HttpMethod.GET, "/x"); 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()); } }