import { test, expect } from "vitest"; import { mkdtemp, rm, mkdir, writeFile, readFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { parse as yamlParse } from "yaml"; import { runPipelineBudgetGenerate, budgetsFilePath, type StepTelemetryRecord, } from "../pipeline-budgets.ts"; import type { KernelCommandInput, KernelRuntimeContext } from "@warpgogol/werkstatt-shared/kernel"; import { createDefaultIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io"; /* RFC-0963: unit tests for pipeline.budget.generate fleet filtering — retired apps are filtered out when not in fleet/fleet.sites.yaml, and empty fleet produces an empty budgets file. 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 pipeline suite's contract for test-file scaffold coverage. */ // @ai-invariant fixtures must not couple to internals beyond the public surface under test const TELEMETRY_RELATIVE_PATH = join( "node_modules", ".cache", "werkstatt", "telemetry", "steps.ndjson", ); const FLEET_SITES_RELATIVE_PATH = join("fleet", "fleet.sites.yaml"); function makeContext(workspaceRoot: string): KernelRuntimeContext { return { io: createDefaultIO().io, workspaceRoot, } as unknown as KernelRuntimeContext; } const baseInput: KernelCommandInput = { argv: [], flags: {}, }; function record(overrides: Partial): StepTelemetryRecord { return { pipeline: "sites-check.author", command: "content.links.validate", app: null, durationMs: 100, timedOut: false, recordedAt: "2026-08-28T00:00:00.000Z", ...overrides, }; } async function writeTelemetry(dir: string, records: StepTelemetryRecord[]): Promise { const filePath = join(dir, TELEMETRY_RELATIVE_PATH); await mkdir(join(dir, "node_modules", ".cache", "werkstatt", "telemetry"), { recursive: true }); const lines = records.map((r) => JSON.stringify(r)).join("\n") + "\n"; await writeFile(filePath, lines); } async function writeFleetSites(dir: string, siteIds: string[]): Promise { const filePath = join(dir, FLEET_SITES_RELATIVE_PATH); await mkdir(join(dir, "fleet"), { recursive: true }); const content = `sites:\n${siteIds.map((id) => ` - id: ${id}`).join("\n")}\n`; await writeFile(filePath, content); } test("retired apps are filtered out when not in fleet.sites.yaml", async () => { const dir = await mkdtemp(join(tmpdir(), "pbg-filter-")); try { await writeTelemetry(dir, [ record({ app: "warpgogol-com", durationMs: 100 }), record({ app: "retired-app", durationMs: 200 }), ]); await writeFleetSites(dir, ["warpgogol-com"]); const result = await runPipelineBudgetGenerate(baseInput, makeContext(dir)); expect(result.exitCode).toBe(0); const budgetsRaw = await readFile(budgetsFilePath(dir), "utf8"); const parsed = yamlParse(budgetsRaw) as { budgets: Array<{ app: string | null }> }; const apps = parsed.budgets.map((b) => b.app); expect(apps).toContain("warpgogol-com"); expect(apps).not.toContain("retired-app"); } finally { await rm(dir, { recursive: true }); } }); test("workspace-scoped records (app=null) are always kept", async () => { const dir = await mkdtemp(join(tmpdir(), "pbg-filter-")); try { await writeTelemetry(dir, [record({ app: null, durationMs: 100 })]); await writeFleetSites(dir, ["warpgogol-com"]); const result = await runPipelineBudgetGenerate(baseInput, makeContext(dir)); expect(result.exitCode).toBe(0); const budgetsRaw = await readFile(budgetsFilePath(dir), "utf8"); const parsed = yamlParse(budgetsRaw) as { budgets: Array<{ app: string | null }> }; expect(parsed.budgets.length).toBe(1); expect(parsed.budgets[0]!.app).toBeNull(); } finally { await rm(dir, { recursive: true }); } }); test("all records filtered out → empty budgets file written", async () => { const dir = await mkdtemp(join(tmpdir(), "pbg-filter-")); try { await writeTelemetry(dir, [ record({ app: "retired-app-1", durationMs: 100 }), record({ app: "retired-app-2", durationMs: 200 }), ]); await writeFleetSites(dir, ["warpgogol-com"]); const result = await runPipelineBudgetGenerate(baseInput, makeContext(dir)); expect(result.exitCode).toBe(0); expect(result.summary).toContain("filtered out"); const budgetsRaw = await readFile(budgetsFilePath(dir), "utf8"); const parsed = yamlParse(budgetsRaw) as { budgets: unknown[] }; expect(parsed.budgets.length).toBe(0); } finally { await rm(dir, { recursive: true }); } }); test("no fleet.sites.yaml → no filtering (backward compatible)", async () => { const dir = await mkdtemp(join(tmpdir(), "pbg-filter-")); try { await writeTelemetry(dir, [record({ app: "any-app", durationMs: 100 })]); // No fleet.sites.yaml written const result = await runPipelineBudgetGenerate(baseInput, makeContext(dir)); expect(result.exitCode).toBe(0); const budgetsRaw = await readFile(budgetsFilePath(dir), "utf8"); const parsed = yamlParse(budgetsRaw) as { budgets: Array<{ app: string | null }> }; expect(parsed.budgets.length).toBe(1); expect(parsed.budgets[0]!.app).toBe("any-app"); } finally { await rm(dir, { recursive: true }); } });