/* Unit tests for materialize config validate covering its 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. RFC-0840: initial unit tests for runMaterializeConfigValidate. */ import { test, expect, beforeEach, afterEach, vi } from "vitest"; import fs from "node:fs/promises"; import path from "node:path"; import os from "node:os"; vi.mock("../sternsystem/registry-io.ts", () => ({ resolveCacheClonePath: vi.fn(), discoverSystems: vi.fn(), })); import { runMaterializeConfigValidate } from "./materialize-config-validate.ts"; import { resolveCacheClonePath, discoverSystems } from "../sternsystem/registry-io.ts"; import { createDefaultIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io"; const mockResolveCacheClonePath = vi.mocked(resolveCacheClonePath); const mockDiscoverSystems = vi.mocked(discoverSystems); let tmpDir: string; beforeEach(async () => { tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "test-mat-config-validate-")); mockResolveCacheClonePath.mockReset(); mockDiscoverSystems.mockReset(); mockDiscoverSystems.mockResolvedValue({ systems: [] } as never); }); afterEach(async () => { await fs.rm(tmpDir, { recursive: true, force: true }); }); function makeContext(workspaceRoot: string) { return { workspaceRoot, logger: { info: () => {}, warn: () => {}, error: () => {} }, io: createDefaultIO().io, actualState: undefined as never, } as never; } test("MAT-CONFIG-01: unrecognized root-level file in workpiece emits warning", async () => { const missionsDir = path.join(tmpDir, "missions", "m001", "workpiece"); await fs.mkdir(missionsDir, { recursive: true }); await fs.writeFile(path.join(missionsDir, "mystery-config.yaml"), "foo: bar\n", "utf8"); await fs.writeFile(path.join(missionsDir, ".lighthouse-budget-ignore"), "pattern\n", "utf8"); const srcDir = path.join(missionsDir, "src"); await fs.mkdir(srcDir, { recursive: true }); await fs.writeFile(path.join(srcDir, "image-delivery.config.yaml"), "rules: []\n", "utf8"); const result = await runMaterializeConfigValidate( { flags: {}, args: [] } as never, makeContext(tmpDir), ); const warnings = result.data!.diagnostics.filter((d) => d.ruleId === "MAT-CONFIG-01"); expect(warnings.length).toBeGreaterThanOrEqual(1); expect(warnings.some((w) => w.message.includes("mystery-config.yaml"))).toBe(true); expect(result.data!.status).toBe("warn"); }); test("MAT-CONFIG-01: unrecognized file in src/ emits warning", async () => { const srcDir = path.join(tmpDir, "missions", "m001", "workpiece", "src"); await fs.mkdir(srcDir, { recursive: true }); await fs.writeFile(path.join(srcDir, "custom-operator.yaml"), "foo: bar\n", "utf8"); const result = await runMaterializeConfigValidate( { flags: {}, args: [] } as never, makeContext(tmpDir), ); const warnings = result.data!.diagnostics.filter((d) => d.ruleId === "MAT-CONFIG-01"); expect(warnings.some((w) => w.message.includes("src/custom-operator.yaml"))).toBe(true); }); test("MAT-CONFIG-01: generated files and boilerplate do NOT emit warnings", async () => { const wpDir = path.join(tmpDir, "missions", "m001", "workpiece"); const srcDir = path.join(wpDir, "src"); await fs.mkdir(srcDir, { recursive: true }); await fs.writeFile(path.join(wpDir, "package.json"), "{}\n", "utf8"); await fs.writeFile(path.join(wpDir, ".gitignore"), "node_modules\n", "utf8"); await fs.writeFile(path.join(srcDir, "content-ref-index.generated.yaml"), "[]\n", "utf8"); await fs.writeFile(path.join(srcDir, "system.md"), "---\n---\n", "utf8"); const result = await runMaterializeConfigValidate( { flags: {}, args: [] } as never, makeContext(tmpDir), ); const warnings = result.data!.diagnostics.filter((d) => d.ruleId === "MAT-CONFIG-01"); expect(warnings).toHaveLength(0); }); test("MAT-CONFIG-02: dead entry in OPERATOR_CONFIG_FILES emits error", async () => { mockDiscoverSystems.mockResolvedValue({ systems: [] } as never); const result = await runMaterializeConfigValidate( { flags: {}, args: [] } as never, makeContext(tmpDir), ); const errors = result.data!.diagnostics.filter((d) => d.ruleId === "MAT-CONFIG-02"); expect(errors.length).toBe(OPERATOR_CONFIG_FILES.length); expect(result.data!.status).toBe("fail"); expect(result.exitCode).toBe(1); }); test("MAT-CONFIG-02: entry found in workpiece does NOT emit error", async () => { const wpDir = path.join(tmpDir, "missions", "m001", "workpiece"); await fs.mkdir(wpDir, { recursive: true }); await fs.writeFile(path.join(wpDir, ".lighthouse-budget-ignore"), "pattern\n", "utf8"); const srcDir = path.join(wpDir, "src"); await fs.mkdir(srcDir, { recursive: true }); await fs.writeFile(path.join(srcDir, "image-delivery.config.yaml"), "rules: []\n", "utf8"); const result = await runMaterializeConfigValidate( { flags: {}, args: [] } as never, makeContext(tmpDir), ); const errors = result.data!.diagnostics.filter((d) => d.ruleId === "MAT-CONFIG-02"); expect(errors).toHaveLength(0); }); test("MAT-CONFIG-01: env.d.ts, middleware.ts, and *.generated.mjs do NOT emit warnings", async () => { const wpDir = path.join(tmpDir, "missions", "m001", "workpiece"); const srcDir = path.join(wpDir, "src"); await fs.mkdir(srcDir, { recursive: true }); await fs.writeFile( path.join(srcDir, "env.d.ts"), '/// \n', "utf8", ); await fs.writeFile(path.join(srcDir, "middleware.ts"), "export default {};\n", "utf8"); await fs.writeFile(path.join(srcDir, "env.schema.generated.mjs"), "export {};\n", "utf8"); const result = await runMaterializeConfigValidate( { flags: {}, args: [] } as never, makeContext(tmpDir), ); const warnings = result.data!.diagnostics.filter((d) => d.ruleId === "MAT-CONFIG-01"); expect(warnings).toHaveLength(0); }); test("clean case: all files recognized, no warnings or errors", async () => { const wpDir = path.join(tmpDir, "missions", "m001", "workpiece"); await fs.mkdir(wpDir, { recursive: true }); await fs.writeFile(path.join(wpDir, ".lighthouse-budget-ignore"), "pattern\n", "utf8"); await fs.writeFile(path.join(wpDir, "package.json"), "{}\n", "utf8"); const srcDir = path.join(wpDir, "src"); await fs.mkdir(srcDir, { recursive: true }); await fs.writeFile(path.join(srcDir, "image-delivery.config.yaml"), "rules: []\n", "utf8"); await fs.writeFile(path.join(srcDir, "system.md"), "---\n---\n", "utf8"); const result = await runMaterializeConfigValidate( { flags: {}, args: [] } as never, makeContext(tmpDir), ); expect(result.data!.diagnostics).toHaveLength(0); expect(result.data!.status).toBe("pass"); expect(result.exitCode).toBe(0); }); const { OPERATOR_CONFIG_FILES } = await import("./operator-config-files.ts");