/* Unit tests for manifest presence 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-0870: initial unit tests for manifest presence check. */ import { test, expect, beforeEach, afterEach } from "vitest"; import { mkdtemp, rm, mkdir, writeFile } from "node:fs/promises"; import { execSync } from "node:child_process"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { runSternsystemValidate } from "./sternsystem-validate.ts"; import { makeInput, makeContext, writeSystemConfig, BASE_SETUP } from "./test-helpers.ts"; let testRoot: string; let workspaceRoot: string; beforeEach(async () => { testRoot = await mkdtemp(join(tmpdir(), "manifest-presence-test-")); workspaceRoot = join(testRoot, "workspace"); await mkdir(workspaceRoot, { recursive: true }); await BASE_SETUP(workspaceRoot); }); afterEach(async () => { await rm(testRoot, { recursive: true, force: true }); }); function initGitRepo(dir: string): void { execSync("git init", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }); execSync("git config user.email test@test.com", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }); execSync("git config user.name Test", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }); } function gitCommit(dir: string): void { execSync("git add -A", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }); execSync("git commit -m test", { cwd: dir, stdio: ["pipe", "pipe", "pipe"] }); } test("STERN-MANIFEST-01 not emitted when manifests are present in HEAD", async () => { await writeSystemConfig(workspaceRoot, [ { path: "../systems-cache/test-bundle", storageType: "non-bare" }, ]); const cacheDir = join(workspaceRoot, "..", "systems-cache", "test-bundle"); initGitRepo(cacheDir); await mkdir(join(cacheDir, "src"), { recursive: true }); await writeFile(join(cacheDir, "src", "image-variants.generated.yaml"), "version: 1\n", "utf8"); await writeFile(join(cacheDir, "src", "video-manifest.generated.yaml"), "version: 1\n", "utf8"); await writeFile( join(cacheDir, "src", "live-video-manifest.generated.yaml"), "version: 1\n", "utf8", ); gitCommit(cacheDir); const result = await runSternsystemValidate(makeInput({}), makeContext(workspaceRoot)); const manifestViolations = result.data?.violations?.filter((v: { rule: string }) => v.rule === "STERN-MANIFEST-01") ?? []; expect(manifestViolations).toHaveLength(0); }); test("STERN-MANIFEST-01 emitted for tracked manifest missing from HEAD", async () => { await writeSystemConfig(workspaceRoot, [ { path: "../systems-cache/test-bundle", storageType: "non-bare" }, ]); const cacheDir = join(workspaceRoot, "..", "systems-cache", "test-bundle"); initGitRepo(cacheDir); await mkdir(join(cacheDir, "src"), { recursive: true }); await writeFile(join(cacheDir, "src", "image-variants.generated.yaml"), "version: 1\n", "utf8"); await writeFile(join(cacheDir, "src", "video-manifest.generated.yaml"), "version: 1\n", "utf8"); await writeFile( join(cacheDir, "src", "live-video-manifest.generated.yaml"), "version: 1\n", "utf8", ); gitCommit(cacheDir); // Remove one manifest and commit the deletion await rm(join(cacheDir, "src", "image-variants.generated.yaml")); gitCommit(cacheDir); const result = await runSternsystemValidate(makeInput({}), makeContext(workspaceRoot)); const manifestViolations = result.data?.violations?.filter((v: { rule: string }) => v.rule === "STERN-MANIFEST-01") ?? []; expect(manifestViolations.length).toBeGreaterThanOrEqual(1); expect(manifestViolations[0]?.message).toContain("image-variants.generated.yaml"); }); test("STERN-MANIFEST-01 not emitted for new systems without tracked manifests", async () => { await writeSystemConfig(workspaceRoot, [ { path: "../systems-cache/test-bundle", storageType: "non-bare" }, ]); const cacheDir = join(workspaceRoot, "..", "systems-cache", "test-bundle"); initGitRepo(cacheDir); // No manifest files at all — new system await writeFile(join(cacheDir, "README.md"), "# test\n", "utf8"); gitCommit(cacheDir); const result = await runSternsystemValidate(makeInput({}), makeContext(workspaceRoot)); const manifestViolations = result.data?.violations?.filter((v: { rule: string }) => v.rule === "STERN-MANIFEST-01") ?? []; expect(manifestViolations).toHaveLength(0); });