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);
});
});