/* Unit tests for registry covering buildRegistry; buildRegistryForModule; deriveModuleBasePath. It also guards the documented public contract and regression-sensitive edge cases. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. Fixtures stay inline and minimal so each case states its own setup. RFC-1311: Compass header sweep — declares the registry suite's contract for test-file scaffold coverage. */ // @ai-invariant fixtures must not couple to kernel internals beyond the public surface under test import { test, expect, describe } from "vitest"; import { buildRegistry, buildRegistryForModule, deriveModuleBasePath } from "../registry.ts"; import type { KernelAppConfig } from "@warpgogol/werkstatt-shared/kernel"; import type { ModuleExport } from "@warpgogol/werkstatt-shared/kernel"; const mockModule: ModuleExport = { name: "test-mod", version: "1.0.0", declarations: [], commands: [ { name: "test.ping", modulePath: "packages/test/src/test.ts", description: "Ping", scope: "workspace", flags: {}, execute: async () => ({ exitCode: 0, ok: true, summary: "pong" }), }, { name: "test.pong", modulePath: "packages/test/src/test.ts", description: "Pong", scope: "workspace", flags: {}, execute: async () => ({ exitCode: 0, ok: true, summary: "ping" }), }, ], pipelines: [ { name: "test.pipeline", steps: [{ command: "test.ping" }, { command: "test.pong" }], }, ], }; const mockConfig: KernelAppConfig = { modules: [mockModule], }; describe("buildRegistry", () => { test("builds actual state from config.modules", async () => { const state = await buildRegistry(mockConfig); expect(state.commands.size).toBe(2); expect(state.commands.has("test.ping")).toBe(true); expect(state.commands.has("test.pong")).toBe(true); }); test("builds actual state from config.moduleLoaders", async () => { const config: KernelAppConfig = { moduleLoaders: { "lazy-mod": async () => mockModule, }, }; const state = await buildRegistry(config); expect(state.commands.size).toBe(2); }); test("throws a descriptive error when a module loader returns undefined", async () => { const config: KernelAppConfig = { moduleLoaders: { "forge-rfc": async () => undefined as unknown as ModuleExport, "lazy-mod": async () => mockModule, }, }; await expect(buildRegistry(config)).rejects.toThrow( /Module loader `forge-rfc` did not return a module.*stale/, ); }); test("throws a descriptive error when a module loader returns a non-module value", async () => { const config: KernelAppConfig = { moduleLoaders: { "bad-mod": async () => "not-a-module" as unknown as ModuleExport, }, }; await expect(buildRegistry(config)).rejects.toThrow( /Module loader `bad-mod` did not return a module/, ); }); test("builds empty actual state when no modules or loaders", async () => { const config: KernelAppConfig = {}; const state = await buildRegistry(config); expect(state.commands.size).toBe(0); }); test("includes pipelines in actual state", async () => { const state = await buildRegistry(mockConfig); expect(state.pipelines.size).toBe(1); expect(state.pipelines.has("test.pipeline")).toBe(true); }); }); describe("buildRegistryForModule", () => { test("builds actual state for a specific module by name", async () => { const state = await buildRegistryForModule(mockConfig, "test-mod"); expect(state.commands.size).toBe(2); }); test("throws when module name not found in modules array", async () => { await expect(buildRegistryForModule(mockConfig, "nonexistent")).rejects.toThrow( /No module named.*nonexistent/, ); }); test("throws a descriptive error when the resolved loader returns undefined", async () => { const config: KernelAppConfig = { moduleLoaders: { "mod-a": async () => undefined as unknown as ModuleExport }, }; await expect(buildRegistryForModule(config, "mod-a")).rejects.toThrow( /Module loader `mod-a` did not return a module/, ); }); test("throws when module loader not found", async () => { const config: KernelAppConfig = { moduleLoaders: { "mod-a": async () => mockModule }, }; await expect(buildRegistryForModule(config, "nonexistent")).rejects.toThrow( /No module loader registered.*nonexistent/, ); }); test("returns empty state when no modules or loaders", async () => { const config: KernelAppConfig = {}; const state = await buildRegistryForModule(config, "anything"); expect(state.commands.size).toBe(0); }); }); describe("deriveModuleBasePath", () => { test("extracts base path up to and including /src", () => { expect(deriveModuleBasePath("packages/engine/src/kernel/runtime/registry.ts")).toBe( "packages/engine/src", ); }); test("returns undefined when no /src/ segment found", () => { expect(deriveModuleBasePath("packages/engine/kernel/registry.ts")).toBeUndefined(); }); test("handles paths with multiple src segments (takes first)", () => { expect(deriveModuleBasePath("packages/engine/src/kernel/src/helpers.ts")).toBe( "packages/engine/src", ); }); test("returns undefined for empty string", () => { expect(deriveModuleBasePath("")).toBeUndefined(); }); test("RFC-1193: extracts base path up to and including /os", () => { expect(deriveModuleBasePath("packages/forge/os/rfc/rfc.module.ts")).toBe("packages/forge/os"); }); test("RFC-1193: first segment wins when both /src/ and /os/ occur", () => { expect(deriveModuleBasePath("packages/a/src/os/helper.ts")).toBe("packages/a/src"); expect(deriveModuleBasePath("packages/a/os/src/helper.ts")).toBe("packages/a/os"); }); });