import type { RuleExpr, RuleLayer } from "@umt/contracts"; import { describe, expect, it } from "vitest"; import { cardsOf, cardToLayer, exprToString, layersToString } from "./expr.ts"; const a = (id: string): RuleExpr => ({ op: "atom", id }); const not = (arg: RuleExpr): RuleExpr => ({ op: "not", arg }); const and = (...args: RuleExpr[]): RuleExpr => ({ op: "and", args }); const or = (...args: RuleExpr[]): RuleExpr => ({ op: "or", args }); describe("规则串序列化", () => { it("只加必要括号,原子大写", () => { expect(exprToString(and(a("specsub"), a("CNSUB")))).toBe("SPECSUB & CNSUB"); expect(exprToString(or(and(a("4K"), a("HDR")), a("1080P")))).toBe("4K & HDR | 1080P"); expect(exprToString(and(or(a("4K"), a("1080P")), not(a("BLU"))))).toBe("(4K | 1080P) & !BLU"); expect(exprToString(not(or(a("REMUX"), a("BLU"))))).toBe("!(REMUX | BLU)"); }); it("多层用 > 连接", () => { const layers: RuleLayer[] = [{ expr: and(a("SPECSUB"), a("CNSUB")) }, { expr: a("BLU") }]; expect(layersToString(layers)).toBe("SPECSUB & CNSUB > BLU"); }); }); describe("层卡片往返", () => { it("两层以内的表达式可转卡片再转回", () => { const layers: RuleLayer[] = [ { expr: and(or(a("4K"), a("1080P")), not(a("BLU"))) }, { expr: not(a("REMUX")) }, { expr: or(a("CNSUB"), not(and(a("HDR"), a("DOLBY")))) }, ]; const cards = cardsOf(layers); expect(cards).not.toBeNull(); expect(cards?.map((c) => cardToLayer(c))).toEqual(layers); }); it("嵌套太深时返回 null,只能在规则串里改", () => { expect(cardsOf([{ expr: and(a("A"), or(a("B"), and(a("C"), a("D")))) }])).toBeNull(); }); it("空层转不出表达式", () => { expect(cardToLayer({ op: "and", items: [] })).toBeNull(); }); });