UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

187 lines (157 loc) 5.19 kB
import { Alepha, z } from "alepha"; import { $action, ServerProvider } from "alepha/server"; import { describe, it } from "vitest"; import { LinkProvider, ServerLinksProvider } from "../index.ts"; class App { ping = $action({ schema: { response: z.object({ pong: z.boolean(), }), }, handler: () => { return { pong: true }; }, }); } describe("LinkProvider", () => { it("should execute action through local handler", async ({ expect }) => { const alepha = Alepha.create().with(App).with(ServerLinksProvider); await alepha.start(); const app = alepha.inject(LinkProvider).client<App>(); expect(await app.ping()).toStrictEqual({ pong: true }); }); it("should expose links endpoint with new registry format", async ({ expect, }) => { const alepha = Alepha.create().with(App).with(ServerLinksProvider); await alepha.start(); const res = await fetch( `${alepha.inject(ServerProvider).hostname}/api/_links`, ); const data = await res.json(); expect(data).toStrictEqual({ prefix: "/api", actions: { ping: { path: "/ping", }, }, }); }); it("should return actions as a map (O(1) lookup by name)", async ({ expect, }) => { const alepha = Alepha.create().with(App).with(ServerLinksProvider); await alepha.start(); const res = await fetch( `${alepha.inject(ServerProvider).hostname}/api/_links`, ); const data = await res.json(); // actions is an object, not an array expect(typeof data.actions).toBe("object"); expect(Array.isArray(data.actions)).toBe(false); expect(data.actions.ping).toBeDefined(); expect(data.actions.ping.path).toBe("/ping"); }); it("should omit method for GET actions", async ({ expect }) => { class GetApp { getUsers = $action({ path: "/users", schema: { response: z.array(z.text()) }, handler: () => ["user1"], }); } const alepha = Alepha.create().with(GetApp).with(ServerLinksProvider); await alepha.start(); const res = await fetch( `${alepha.inject(ServerProvider).hostname}/api/_links`, ); const data = await res.json(); expect(data.actions.getUsers.method).toBeUndefined(); }); it("should include method for non-GET actions", async ({ expect }) => { class PostApp { createUser = $action({ method: "POST", path: "/users", schema: { body: z.object({ name: z.text() }), response: z.text(), }, handler: ({ body }) => body.name, }); } const alepha = Alepha.create().with(PostApp).with(ServerLinksProvider); await alepha.start(); const res = await fetch( `${alepha.inject(ServerProvider).hostname}/api/_links`, ); const data = await res.json(); expect(data.actions.createUser.method).toBe("POST"); }); it("should not include definitions or schema refs in registry", async ({ expect, }) => { class SchemaApp { getUser = $action({ path: "/users/:id", schema: { response: z.object({ id: z.integer(), name: z.text() }) }, handler: () => ({ id: 1, name: "John" }), }); updateUser = $action({ method: "PUT", path: "/users/:id", schema: { body: z.object({ name: z.text() }), response: z.object({ id: z.integer(), name: z.text() }), }, handler: () => ({ id: 1, name: "John" }), }); } const alepha = Alepha.create().with(SchemaApp).with(ServerLinksProvider); await alepha.start(); const res = await fetch( `${alepha.inject(ServerProvider).hostname}/api/_links`, ); const data = await res.json(); expect(data.definitions).toBeUndefined(); expect(data.actions.getUser.body).toBeUndefined(); expect(data.actions.getUser.response).toBeUndefined(); expect(data.actions.updateUser.body).toBeUndefined(); expect(data.actions.updateUser.response).toBeUndefined(); }); it("should not include group or rawSchema in wire format", async ({ expect, }) => { const alepha = Alepha.create().with(App).with(ServerLinksProvider); await alepha.start(); const res = await fetch( `${alepha.inject(ServerProvider).hostname}/api/_links`, ); const data = await res.json(); const action = data.actions.ping; expect(action.group).toBeUndefined(); expect(action.rawSchema).toBeUndefined(); expect(action.name).toBeUndefined(); // name is the key, not a field expect(action.requestBodyType).toBeUndefined(); }); it("should throw on duplicate local action names", async ({ expect }) => { class DuplicateApp { ping = $action({ handler: () => "first", }); ping2 = $action({ name: "ping", handler: () => "second", }); } const alepha = Alepha.create().with(DuplicateApp).with(ServerLinksProvider); try { await alepha.start(); expect.unreachable("should have thrown"); } catch (error: any) { expect(error.cause?.message).toMatch(/Duplicate action name "ping"/); } }); });