Files
miot_x/tests/compiler.test.ts

95 lines
3.6 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { readDeviceInventoryFile, readBackupFile } from "../src/index.ts";
import { collectRuleDefinitions, compileRule, compileWorkflow } from "../src/compiler.ts";
import examples, { conditionBeforeAction, eventTurnOn, mergedTriggers } from "../examples/rules/index.ts";
const now = 1_760_000_000_000;
const devices = readDeviceInventoryFile("resources/devices.json");
describe("规则编译", () => {
test("单条事件触发开灯规则生成稳定 JSON", () => {
const { rule } = compileRule(eventTurnOn, { now });
expect(rule).toEqual({
id: String(now),
cfg: {
id: String(now),
userData: {
name: "示例-事件触发开灯",
transform: { x: 0, y: 0, scale: 1, rotate: 0 },
lastUpdateTime: now,
version: 0,
},
uiType: "test",
enable: true,
},
nodes: [
{
id: "DI1",
type: "deviceInput",
props: {
did: "blt.3.1htiptpdgco00",
siid: 2,
eiid: 1008,
},
inputs: {},
outputs: { output: ["DO1.trigger"] },
cfg: {
urn: "urn:miot-spec-v2:device:motion-sensor:0000A014:xiaomi-pir1:2",
name: "deviceInput",
version: 1,
pos: { x: 240, y: 180, width: 450, height: 206 },
},
},
{
id: "DO1",
type: "deviceOutput",
props: {
did: "group.1815373077765824512",
siid: 2,
piid: 1,
value: true,
},
inputs: { trigger: null },
outputs: { output: [] },
cfg: {
urn: "urn:miot-spec-v2:device:light:0000A001:mijia-group3:3:0000C802",
name: "deviceOutput",
version: 1,
pos: { x: 800, y: 180, width: 528, height: 164 },
},
},
],
});
});
test("状态判断后动作会生成 deviceGet 并连接满足路径", () => {
const { rule } = compileRule(conditionBeforeAction, { now });
expect(rule.nodes.map((node) => node.type)).toEqual(["deviceInput", "deviceGet", "deviceOutput"]);
expect(rule.nodes[0]!.outputs.output).toEqual(["DG1.input"]);
expect(rule.nodes[1]!.outputs.output).toEqual(["DO1.trigger"]);
expect(rule.nodes[1]!.outputs.output2).toEqual([]);
});
test("多个触发器会生成 signalOr 合并节点", () => {
const { rule } = compileRule(mergedTriggers, { now });
expect(rule.nodes.map((node) => node.type)).toEqual(["deviceInput", "deviceInput", "signalOr", "deviceOutput"]);
expect(rule.nodes[0]!.outputs.output).toEqual(["SO1.input0"]);
expect(rule.nodes[1]!.outputs.output).toEqual(["SO1.input1"]);
expect(rule.nodes[2]!.outputs.output).toEqual(["DO1.trigger"]);
});
test("模块导出收集支持默认数组和命名规则", async () => {
const moduleExports = await import("../examples/rules/index.ts");
const definitions = collectRuleDefinitions(moduleExports);
expect(definitions.map((definition) => definition.name)).toEqual(examples.map((definition) => definition.name));
});
test("编译并追加到真实备份时保留原有规则", () => {
const base = readBackupFile("resources/备份2026_4_26 19_17_42.bak");
const result = compileWorkflow([eventTurnOn], base, { now, devices });
expect(result.diagnostics.filter((diagnostic) => diagnostic.severity === "error")).toEqual([]);
expect(result.backup.rules).toHaveLength(base.rules.length + 1);
expect(result.backup.rules.at(-1)?.cfg.userData.name).toBe("示例-事件触发开灯");
});
});