Files
Ekklesia/packages/module-sdk/tests/define-module-runtime.test.ts
T
ekklesiaandClaude Sonnet 5 e296e083ec feat: define module HTTP routes
Phase 5B, pass 1: framework-neutral module HTTP route contracts and runtime
registration. Adds @ekklesia/contracts' module-http.ts (methods, canonical
path grammar, route definition/context/handler types, and validation)
and Core's module runtime staging/publication of a module's declared
routes alongside its capabilities, gated on the module's own manifest
permissions. Routes are collected into StartedModuleRuntime as an
immutable snapshot but are not yet mounted into apps/api; that is pass 2.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-07-24 18:36:23 -05:00

70 lines
2.0 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
defineModuleRuntime,
isModuleHttpMethod,
MODULE_HTTP_METHODS,
type ModuleHttpRouteRegistrar,
type ModuleRegistrationContext,
} from "../src/index.js";
describe("defineModuleRuntime", () => {
it("defines a frozen server runtime without duplicating the static manifest", async () => {
const start = vi.fn();
const runtime = defineModuleRuntime({
id: "example",
start,
version: "0.1.0",
});
expect(runtime).toEqual({ id: "example", start, version: "0.1.0" });
expect(Object.isFrozen(runtime)).toBe(true);
});
it("rejects invalid runtime definitions", () => {
expect(() =>
defineModuleRuntime({
id: "Not Valid",
version: "0.1.0",
}),
).toThrow("Invalid Ekklesia module runtime definition.");
});
it("re-exports the module-http contract so a module's register hook can declare routes", async () => {
expect(MODULE_HTTP_METHODS).toContain("GET");
expect(isModuleHttpMethod("GET")).toBe(true);
expect(isModuleHttpMethod("get")).toBe(false);
const registeredRoutes: unknown[] = [];
const http: ModuleHttpRouteRegistrar = {
route: (definition) => {
registeredRoutes.push(definition);
},
};
const runtime = defineModuleRuntime({
id: "example",
version: "0.1.0",
register({ http: registrar }) {
registrar.route({
method: "GET",
path: "/records",
permission: "example.records.read",
handler: () => new Response(null, { status: 204 }),
});
},
});
const context: ModuleRegistrationContext = {
capabilities: {
provide: () => undefined,
resolve: () => undefined,
},
http,
moduleId: "example",
};
await runtime.register?.(context);
expect(registeredRoutes).toHaveLength(1);
expect(registeredRoutes[0]).toMatchObject({ method: "GET", path: "/records" });
});
});