alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
335 lines (265 loc) • 9.22 kB
text/typescript
import { Alepha, z } from "alepha";
import { describe, it } from "vitest";
import { $action, $route, ServerRouterProvider } from "../index.ts";
describe("ServerRouterProvider - getRoutes", () => {
it("should return all routes when no pattern is provided", async ({
expect,
}) => {
const alepha = Alepha.create();
class TestApp {
action1 = $action({
path: "/users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
action2 = $action({
path: "/posts",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
route1 = $route({
path: "/health",
handler: () => "OK",
});
}
await alepha.with(TestApp).start();
const routerProvider = alepha.inject(ServerRouterProvider);
const routes = routerProvider.getRoutes();
expect(routes.length).toBeGreaterThanOrEqual(3);
});
it("should return routes matching wildcard pattern", async ({ expect }) => {
const alepha = Alepha.create();
class TestApp {
users = $action({
path: "/users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
userDetail = $action({
path: "/users/:id",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
posts = $action({
path: "/posts",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
health = $route({
path: "/health",
handler: () => "OK",
});
}
await alepha.with(TestApp).start();
const routerProvider = alepha.inject(ServerRouterProvider);
const routes = routerProvider.getRoutes("/api/*");
// Should include the actions under /api/*
const paths = routes.map((r) => r.path);
expect(paths.length).toBeGreaterThanOrEqual(3);
expect(paths.some((p) => p.includes("/users"))).toBe(true);
expect(paths.some((p) => p.includes("/posts"))).toBe(true);
// health route should not match /api/*
expect(paths.some((p) => p === "/GET/health")).toBe(false);
});
it("should return empty array when pattern doesn't match any routes", async ({
expect,
}) => {
const alepha = Alepha.create();
class TestApp {
users = $action({
path: "/users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
posts = $action({
path: "/posts",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
}
await alepha.with(TestApp).start();
const routerProvider = alepha.inject(ServerRouterProvider);
const routes = routerProvider.getRoutes("/v2/*");
expect(routes).toHaveLength(0);
});
it("should return empty array when exact pattern doesn't match", async ({
expect,
}) => {
const alepha = Alepha.create();
class TestApp {
users = $action({
path: "/users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
}
await alepha.with(TestApp).start();
const routerProvider = alepha.inject(ServerRouterProvider);
const routes = routerProvider.getRoutes("/admin");
expect(routes).toHaveLength(0);
});
it("should differentiate between exact match and wildcard match patterns", async ({
expect,
}) => {
const alepha = Alepha.create();
class TestApp {
apiInfo = $action({
path: "/api-info",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
apiUsers = $action({
path: "/api-users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
settings = $route({
path: "/settings",
handler: () => "OK",
});
}
await alepha.with(TestApp).start();
const routerProvider = alepha.inject(ServerRouterProvider);
// Wildcard pattern matches multiple routes
const wildcardRoutes = routerProvider.getRoutes("/api/*");
expect(wildcardRoutes.length).toBeGreaterThan(0);
});
it("should return multiple routes for deep nested paths", async ({
expect,
}) => {
const alepha = Alepha.create();
class TestApp {
level1 = $action({
path: "/v1/users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
level2 = $action({
path: "/v1/users/:id/posts",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
level3 = $action({
path: "/v1/users/:id/posts/:postId/comments",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
v2 = $action({
path: "/v2/users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
}
await alepha.with(TestApp).start();
const routerProvider = alepha.inject(ServerRouterProvider);
const routes = routerProvider.getRoutes("/api/*");
// Should find nested routes under /api/*
expect(routes.length).toBeGreaterThanOrEqual(2);
const paths = routes.map((r) => r.path);
expect(paths.some((p) => p.includes("v1"))).toBe(true);
expect(paths.some((p) => p.includes("v2"))).toBe(true);
});
it("should match all routes with root wildcard pattern", async ({
expect,
}) => {
const alepha = Alepha.create();
class TestApp {
health = $route({
path: "/health",
schema: { response: z.object({ status: z.string() }) },
handler: () => ({ status: "ok" }),
});
about = $route({
path: "/about",
schema: { response: z.object({ name: z.string() }) },
handler: () => ({ name: "app" }),
});
users = $action({
path: "/users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
}
await alepha.with(TestApp).start();
const routerProvider = alepha.inject(ServerRouterProvider);
const routes = routerProvider.getRoutes("/*");
expect(routes.length).toBeGreaterThanOrEqual(3);
});
it("should correctly filter by path prefix", async ({ expect }) => {
const alepha = Alepha.create();
class TestApp {
publicApi = $action({
path: "/public/info",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
adminApi = $action({
path: "/admin/users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
privateRoute = $route({
path: "/private",
handler: () => "private",
});
}
await alepha.with(TestApp).start();
const routerProvider = alepha.inject(ServerRouterProvider);
// Get routes matching /api/* pattern
const apiRoutes = routerProvider.getRoutes("/api/*");
expect(apiRoutes.length).toBeGreaterThan(0);
// Get routes matching a specific non-api pattern
const privateRoutes = routerProvider.getRoutes("/private");
expect(privateRoutes.length).toBeGreaterThanOrEqual(0);
});
it("should handle routes with method-specific paths", async ({ expect }) => {
const alepha = Alepha.create();
class TestApp {
getUsers = $action({
method: "GET",
path: "/users",
schema: { response: z.array(z.object({ id: z.integer() })) },
handler: () => [],
});
createUser = $action({
method: "POST",
path: "/users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
updateUser = $action({
method: "PATCH",
path: "/users/:id",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
}
await alepha.with(TestApp).start();
const routerProvider = alepha.inject(ServerRouterProvider);
const routes = routerProvider.getRoutes("/api/*");
// Should return routes with different HTTP methods for /users path
expect(routes.length).toBeGreaterThanOrEqual(2);
const paths = routes.map((r) => r.path);
expect(paths.some((p) => p.includes("/users"))).toBe(true);
});
it("should be case-sensitive for path matching", async ({ expect }) => {
const alepha = Alepha.create();
class TestApp {
users = $action({
path: "/users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
users2 = $action({
path: "/Users",
schema: { response: z.object({ id: z.integer() }) },
handler: () => ({ id: 1 }),
});
}
await alepha.with(TestApp).start();
const routerProvider = alepha.inject(ServerRouterProvider);
const allRoutes = routerProvider.getRoutes();
// Both routes should exist
expect(allRoutes.length).toBeGreaterThanOrEqual(2);
});
});