Files
minimize-tools/tests/minimize/engine.test.ts
T
2026-09-01 13:22:35 +02:00

129 lines
4.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { minimizeInput } from "../../src/minimize/engine";
import {
createPredicate,
type RegexEvaluator,
} from "../../src/minimize/predicates";
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 = `<case><noise a="1">discard</noise><payload><message>BOOM</message><also>discard</also></payload></case>`;
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:
'<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:include href="https://invalid/x.xsl"/></xsl:stylesheet>',
},
unusedRegex,
),
).toThrow(/include/u);
});
});