import { describe, expect, it } from "vitest"; import { minimizeInput } from "../../src/minimize/engine"; import { createPredicate, type RegexEvaluator, } from "../../src/minimize/predicates"; import { bundlePredicateAdapter, minimizeBundle, } from "../../src/minimize/bundle"; const unusedRegex: RegexEvaluator = async () => ({ matched: false, elapsedMs: 0, timedOut: false, }); describe("minimization engine", () => { it("removes XML structure while preserving a literal marker", async () => { const input = `discardBOOMdiscard`; const predicate = createPredicate( { kind: "contains", needle: "BOOM" }, unusedRegex, ); const result = await minimizeInput( input, { structure: "xml", maxTests: 300, maxSeconds: 5 }, predicate, new AbortController().signal, ); expect(result.minimized).toContain("BOOM"); expect(result.minimized.length).toBeLessThan(input.length); expect( new DOMParser() .parseFromString(result.minimized, "application/xml") .querySelector("parsererror"), ).toBeNull(); expect(result.steps.length).toBeGreaterThan(0); }); it("keeps the original JSON Schema failure signature", async () => { const input = JSON.stringify({ request: { id: 0, label: "noise" }, unrelated: [1, 2, 3], }); const predicate = createPredicate( { kind: "json-schema-fails", preserveFailureSignature: true, schema: JSON.stringify({ type: "object", required: ["request"], properties: { request: { type: "object", required: ["id"], properties: { id: { type: "integer", minimum: 1 } }, }, }, }), }, unusedRegex, ); const result = await minimizeInput( input, { structure: "json", maxTests: 500, maxSeconds: 5 }, predicate, new AbortController().signal, ); expect(JSON.parse(result.minimized)).toEqual({ request: { id: 0 } }); }); it("minimizes malformed syntax and reports budget exhaustion", async () => { const predicate = createPredicate({ kind: "invalid-json" }, unusedRegex); const result = await minimizeInput( '{"large": [1,2,}', { structure: "json", maxTests: 1, maxSeconds: 5 }, predicate, new AbortController().signal, ); expect(result.exhausted).toBe(true); expect(result.tests).toBe(1); }); it("rejects a baseline that does not reproduce and honours cancellation", async () => { await expect( minimizeInput( "safe", { structure: "text", maxTests: 20, maxSeconds: 2 }, createPredicate({ kind: "contains", needle: "BOOM" }, unusedRegex), new AbortController().signal, ), ).rejects.toThrow(/does not satisfy/u); const controller = new AbortController(); controller.abort(); await expect( minimizeInput( "BOOM", { structure: "text", maxTests: 20, maxSeconds: 2 }, createPredicate({ kind: "contains", needle: "BOOM" }, unusedRegex), controller.signal, ), ).rejects.toMatchObject({ name: "AbortError" }); }); it("uses isolated regex outcomes and fails closed on unsafe schemas/XSLT", async () => { const slow = createPredicate( { kind: "regex-slow", pattern: "a+", flags: "u", thresholdMs: 20 }, async () => ({ matched: false, elapsedMs: 20, timedOut: false }), ); expect(await slow("aaaa", new AbortController().signal)).toBe(true); expect(() => createPredicate( { kind: "json-schema-fails", schema: '{"$ref":"https://invalid/schema.json"}', }, unusedRegex, ), ).toThrow(/unsupported.*\$ref/iu); expect(() => createPredicate( { kind: "xslt-throws", stylesheet: '', }, unusedRegex, ), ).toThrow(/include/u); }); it("samples a flaky predicate according to an explicit stability policy", async () => { const observations = new Map(); const result = await minimizeInput( "noise BOOM noise", { structure: "text", maxTests: 300, maxSeconds: 5, stability: { samples: 3, requiredPasses: 2 }, }, async (candidate) => { const count = (observations.get(candidate) ?? 0) + 1; observations.set(candidate, count); return candidate.includes("BOOM") && count % 3 !== 0; }, new AbortController().signal, ); expect(result.minimized).toContain("BOOM"); expect(result.stability).toEqual({ samples: 3, requiredPasses: 2 }); expect(result.unstableCandidates).toBeGreaterThan(0); }); it("removes files and then minimizes retained bundle content", async () => { const predicate = bundlePredicateAdapter( createPredicate({ kind: "contains", needle: "BOOM" }, unusedRegex), "concatenated", ); const result = await minimizeBundle( [ { path: "noise.txt", content: "discard all of this" }, { path: "case/input.txt", content: "prefix BOOM suffix" }, ], { maxTests: 400, maxSeconds: 5 }, predicate, new AbortController().signal, ); expect(result.minimized).toHaveLength(1); expect(result.minimized[0]).toMatchObject({ path: "case/input.txt" }); expect(result.minimized[0]?.content).toContain("BOOM"); expect(result.steps.some((step) => step.stage === "files")).toBe(true); expect(result.steps.some((step) => step.stage === "content")).toBe(true); }); });