import { describe, expect, it } from "@suaveplan/testing/runner"; import { parsePipeline } from "../src/pipeline/parser.js"; /** * Property-style tests: the same op chain produced via the pipe-form string * and via the positional bare-op tokens MUST yield the same internal edit * chain. This is the parser invariant that lets the bare-op CLI form and * `batch --pipeline` share a single downstream code path. */ describe("parsePipeline determinism", () => { const cases: Array<{ str: string; tokens: readonly string[] }> = [ { str: "trim 30s", tokens: ["trim", "30s"] }, { str: "normalize -16lufs", tokens: ["normalize", "-16lufs"] }, { str: "trim 30s | normalize -16lufs", tokens: ["trim", "30s", "normalize", "-16lufs"], }, { str: "trim 30s | normalize | fade 0.5s 1s", tokens: ["trim", "30s", "normalize", "fade", "0.5s", "1s"], }, { str: "gain 6db", tokens: ["gain", "6db"] }, ]; for (const { str, tokens } of cases) { it(`pipe-form and bare-op produce the same chain for: ${str}`, { timeout: 5000, }, () => { expect(parsePipeline(tokens)).toEqual(parsePipeline(str)); }); } it("idempotent: parse(parse(x)) is the identity for chained pipe forms", { timeout: 5000, }, () => { const once = parsePipeline("trim 30s | normalize -16lufs"); const reSerialised = once .map(([name, opts]) => { const args = Object.entries(opts).map(([k, v]) => { if (k === "start") return `${v}s`; if (k === "lufs") return `${v}lufs`; return `${k}=${v}`; }); return [name, ...args].join(" "); }) .join(" | "); const twice = parsePipeline(reSerialised); expect(twice).toEqual(once); }); });