Is there any trick for how to create an empty Rout...
# http4k
l
Is there any trick for how to create an empty RoutingHttpHandler? When I try to anything like
routes(emptyList())
I get:
Exception in thread "main" java.lang.IllegalArgumentException: No routes added!
at org.http4k.routing.RoutingHandler.<init>(RoutingHandler.kt:19)
at org.http4k.routing.RoutingHttpHandler.<init>(http.kt:23)
My use case is assembling routes from different "route providers" that may or not produce any routes. For example, I have a "ClockProvider" that, when configured for test, exposes an endpoint to set a controlled clock and otherwise exposes no such endpoint.
Copy code
routes(
  "/api" bind routes(
    "/login" bind Method.POST to authenticationEndpoints.login,
  ),
  clockProvider.routes, // trying to sometimes return no routes here
  uiEndpoints.routes,
)
j
One possible downside of that approach is that you cannot run multiple parallel tests, as there is only one time in the system. A different approach might be to have a per-request clock, which could be set in a filter. Various mechanisms could be used to ensure that this filter wasn't available in non-test environments.
That's not really answering your question, I realise.
d
you can use an
orElse bind { Response(NOT_FOUND} }
as a default
l
@James Richardson in my case that's not an issue (this test suite spins up a system for each test, it's plenty fast enough) but that's a good appraoch for other scenarios I might encounter, I appreciate the suggestion!
@dave the only issue with using orElse as a noop is it doesn't fall through to other routes. In my example above the routes from
uiEndpoints.routes
wouldn't have a chance to match. Is there a way to create a RoutingHttpHandler that doesn't match anything?
j
Ah, I was assuming that this was a long lived services that was actually running on a remote server, so the interaction had to be via http. If this is an in-memory test, and you can start it all for each test, which is great, the you could avoid all this by Initialising the whole thing with a fake/controllable Clock that the test can control with just a method call.
l
Yeah that's exactly what I'm doing! I left out some of this context in my initial message because I didn't think it was necessary for my question but since you're taking an interest (which I'm enjoying) here's a bit more. This test suite is exercising our software system through its web UI (via playwright) so the http4k bits are running with a real server (not just the HttpHandler). Playwright has a built-in way to configure the time in the browser, and I've written a simple helper that posts to /test/clock to let the server know when the time has changed to coincide with the time set in the browser, in support of the time-based scenarios I'm testing. For now, in non-test mode I'm having my "clock routes" just return some unused route like:
Copy code
routes("/test/clock" bind Method.POST to { Response(Status.FORBIDDEN) })
so that the server's regular fallback routes can catch what they're supposed to. But I'd rather find a way to instantiate a RoutingHttpHandler that truly doesn't match anything as that makes the intention a little clearer (for future me, or other team members). I'm also reconsidering your suggestion to use an HTTP Header instead of an HttpHandler to communicate the time from tests to server and moving my Clock from DI to a RequestContext. But that would require more changes just for test within our UI layer (which is what calls the http4k routes), and maybe I'm being stubborn but I feel like there should be a way to instantiate a RoutingHttpHandler that doesn't match any routes and I just haven't figured it out.
d
Another option is to provide a "processing time" in the message payload and to use that instead of the internal clock - this is obviously only suitable for internal trusted services but it does work well if you can get away with it.