-
-
Notifications
You must be signed in to change notification settings - Fork 603
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unable to add middleware after creation: Can not add a route since the matcher is already built. #3026
Comments
Hi @JoaquimLey It's not a bug. As the error message, you can not add routes after handling a response. So, instantiate Hono in |
Yes I was also unsure if that was the right template for the issue. I am, the app already exists (or else I wouldn’t be able to add the middleware, all my tests run after the hono app is created. In a nutshell I’m looking for a way to inject “mock data” into the context in my tests. Let’s say that the bearer middleware injects the decoded user, how can I verify that the right data (eg user.id) was passed down to another layer (use case, service, repository) from the router? |
Thank you for you explanation. Understood. @usualoma Do you have any thoughts? |
Hi @JoaquimLey Is it possible to rewrite your test as follows? describe("Auth router - [POST] /email/confirm", () => {
let authRouter;
beforeEach(() => {
authRouter = new MyApp();
authRouter.use("/email/confirm", async (c, next) => {
console.log("Setting mock user in test middleware");
const mockUser = { sub: "mock_id", email: "[email protected]" }
c.set("user", mockUser)
});
});
it("It should return Hello world", async () => {
const mock_access_token = "mock_access_token";
const res = await authRouter.request(
"/email/confirm",
{
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${mock_access_token}` },
},
{}
);
expect(res.status).toBe(200)
const data = await res.text();
expect(data).toEqual("Hello World");
});
}); |
Hello @usualoma Not sure how I would create the app Here's how it is working right now: // auth.router.ts
const authRouter = new Hono<{ Bindings: Bindings }>();
authRouter.use("*", authMiddleware); // Injects service
authRouter.onError((error, c) => {
// TODO: Hookup an actual error logger.
console.error("Error on auth router ", error);
if (error instanceof HTTPException) {
return error.getResponse();
}
return c.json({ message: "Server error, please try again later\\." }, 500);
});
////////////
// Routes //
////////////
authRouter.post("/email/confirm", async (c) => { Maybe I should've been more clear but his is being imported by the "root" hono app, I'm following the pattern of adding controllers (https://hono.dev/docs/guides/best-practices#building-a-larger-application): // index.ts
import authRouter from "@/services/auth/auth.router";
(...)
const app = new Hono<{ Bindings: Env }>().basePath(API_VERSION);
(...)
app.route("auth", authRouter); EDIT:After trying to figure this out for some time now I'm wondering if we should have a test-util that injects/manipulates the context. |
Hi @JoaquimLey import authRouter from "@/services/auth/auth.router";
describe("Auth router - [POST] /email/confirm", () => {
let app;
beforeEach(() => {
app = new Hono();
app.route("/email/*", authRouter);
app.use("/email/confirm", async (c, next) => {
console.log("Setting mock user in test middleware");
const mockUser = { sub: "mock_id", email: "[email protected]" }
c.set("user", mockUser)
});
});
it("It should return Hello world", async () => {
const mock_access_token = "mock_access_token";
const res = await app.request(
"/email/confirm",
{
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${mock_access_token}` },
},
{}
);
expect(res.status).toBe(200)
const data = await res.text();
expect(data).toEqual("Hello World");
});
}); |
Hello @usualoma I was able to get it working! Your example above is also a better overall setup, that way it'll further isolate the router tests since it won't have to deal with any potential middleware(s) from the I had to make some small changes though: describe("Auth router - [POST] /email/confirm", () => {
let app: Hono;
beforeEach(() => {
app = new Hono();
// IMPORTANT: Add the middleware before app.route
// Added "*" for the sake of simplicity, we can still set specific routes.
app.use("*", async (c, next) => {
const mockUser = { sub: "mock_id", email: "[email protected]" };
c.set("jwt", mockUser);
await next();
});
app.route("auth", authRouter);
});
it("It should return Hello world", async () => {
const mock_access_token = "mock_access_token";
const res = await app.request(
"/auth/email/confirm",
{
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${mock_access_token}` },
},
{}
);
expect(res.status).toBe(200);
const data = await res.text();
expect(data).toEqual("Hello World");
});
}); I think the most relevant part is adding the middleware before any routing. I'm wondering if it would be possible to write a small test util to wrap/abstract all this setup, the biggest potential issue I see is requiring the consumer to use the resuling "app" object, but maybe we could do something along the lines of a If this makes sense I'm happy to try and take a look or just write a small package that can do this, regardless I would suggest adding this example in the docs, once I get the time I'll create a PR that you can either use or at least get some inspiration from. PS: Feel free to close this issue, ty for your time and helping out! |
Adding this WIP PR: |
@usualoma Thank you! @JoaquimLey As you said, I'll close this issue now and review your PR for the website if it's ready. Thanks! |
What version of Hono are you using?
4.4.5
What runtime/platform is your app running on?
Bun
What steps can reproduce the bug?
Bootstrap the app like this
Then during testing try to add another middleware (this is after the app is instantiated):
What is the expected behavior?
I was hoping to have the "test middleware" added to this instance so I could manipulate what the user object is (I decode the JWT and set it
c.set("user", decodedUser)
But during testing I want to have full control of what is on my context, this includes the data that was decoded from the mock jwt.
What do you see instead?
An error is thrown:
Additional information
No response
The text was updated successfully, but these errors were encountered: