配套 service 候选 500a9a2: - flow.ts 增加 admin/def/offlineLine、enableLine(T03 线级停发/恢复) - Defs.vue 接线级停发开关与状态展示;lineOffline.ts 收敛判定与文案 - AssignPreview.vue + assignEmbed.ts 发起时指派预览(T02 契约) - def-editor 表单坐标绑定微调 - 新增 vitest 组件测试与 line-offline 契约测试(npm test 可跑)
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { lineActionErrorDetail, runLineAction } from "../src/utils/lineOffline";
|
||||
|
||||
/** 线级启停纯逻辑单测:不发真实请求,边界=请求/刷新编排,不代表后端或真实 UI 已验证 */
|
||||
|
||||
test("停发成功:按 flowGuid 调一次接口,按返回行数刷新", async () => {
|
||||
const calls: string[] = [];
|
||||
let reloads = 0;
|
||||
const out = await runLineAction({
|
||||
flowGuid: " g1 ",
|
||||
lineOffline: 0,
|
||||
action: "offline",
|
||||
request: async (guid) => { calls.push(guid); return 3; },
|
||||
reload: async () => { reloads++; },
|
||||
});
|
||||
assert.deepEqual(calls, ["g1"]);
|
||||
assert.equal(out.kind, "updated");
|
||||
assert.equal(out.affected, 3);
|
||||
assert.equal(out.reloadFailed, false);
|
||||
assert.equal(reloads, 1);
|
||||
});
|
||||
|
||||
test("恢复成功:已停发线才发恢复请求", async () => {
|
||||
let requested = "";
|
||||
let reloads = 0;
|
||||
const out = await runLineAction({
|
||||
flowGuid: "g2",
|
||||
lineOffline: 1,
|
||||
action: "enable",
|
||||
request: async (guid) => { requested = guid; return 2; },
|
||||
reload: async () => { reloads++; },
|
||||
});
|
||||
assert.equal(requested, "g2");
|
||||
assert.equal(out.kind, "updated");
|
||||
assert.equal(out.affected, 2);
|
||||
assert.equal(reloads, 1);
|
||||
});
|
||||
|
||||
test("重复停发被前端状态守卫拦下:不发请求、不刷新", async () => {
|
||||
let requests = 0;
|
||||
let reloads = 0;
|
||||
const out = await runLineAction({
|
||||
flowGuid: "g3",
|
||||
lineOffline: 1,
|
||||
action: "offline",
|
||||
request: async () => { requests++; return 1; },
|
||||
reload: async () => { reloads++; },
|
||||
});
|
||||
assert.equal(out.kind, "skipped");
|
||||
assert.equal(out.reason, "already-offline");
|
||||
assert.equal(requests, 0);
|
||||
assert.equal(reloads, 0);
|
||||
});
|
||||
|
||||
test("恢复一条未停发的线被拦下:不发请求、不刷新", async () => {
|
||||
let requests = 0;
|
||||
const out = await runLineAction({
|
||||
flowGuid: "g4",
|
||||
lineOffline: 0,
|
||||
action: "enable",
|
||||
request: async () => { requests++; return 1; },
|
||||
reload: async () => {},
|
||||
});
|
||||
assert.equal(out.kind, "skipped");
|
||||
assert.equal(out.reason, "not-offline");
|
||||
assert.equal(requests, 0);
|
||||
});
|
||||
|
||||
test("空 flowGuid 不发请求(历史脏行防误操作整线)", async () => {
|
||||
let requests = 0;
|
||||
const out = await runLineAction({
|
||||
flowGuid: "",
|
||||
lineOffline: 0,
|
||||
action: "offline",
|
||||
request: async () => { requests++; return 1; },
|
||||
reload: async () => {},
|
||||
});
|
||||
assert.equal(out.kind, "skipped");
|
||||
assert.equal(out.reason, "no-guid");
|
||||
assert.equal(requests, 0);
|
||||
});
|
||||
|
||||
test("接口返回 0 行:报 empty 但仍刷新列表以同步真实状态", async () => {
|
||||
let reloads = 0;
|
||||
const out = await runLineAction({
|
||||
flowGuid: "g5",
|
||||
lineOffline: 0,
|
||||
action: "offline",
|
||||
request: async () => 0,
|
||||
reload: async () => { reloads++; },
|
||||
});
|
||||
assert.equal(out.kind, "empty");
|
||||
assert.equal(out.affected, 0);
|
||||
assert.equal(reloads, 1);
|
||||
});
|
||||
|
||||
test("接口失败向上抛出,且不触发刷新", async () => {
|
||||
let reloads = 0;
|
||||
await assert.rejects(() => runLineAction({
|
||||
flowGuid: "g6",
|
||||
lineOffline: 0,
|
||||
action: "offline",
|
||||
request: async () => { throw new Error("无流程管理权限"); },
|
||||
reload: async () => { reloads++; },
|
||||
}), /无流程管理权限/);
|
||||
assert.equal(reloads, 0);
|
||||
});
|
||||
|
||||
test("操作成功但刷新失败:保留成功结果并标记 reloadFailed", async () => {
|
||||
const out = await runLineAction({
|
||||
flowGuid: "g7",
|
||||
lineOffline: 1,
|
||||
action: "enable",
|
||||
request: async () => 1,
|
||||
reload: async () => { throw new Error("网络异常"); },
|
||||
});
|
||||
assert.equal(out.kind, "updated");
|
||||
assert.equal(out.affected, 1);
|
||||
assert.equal(out.reloadFailed, true);
|
||||
});
|
||||
|
||||
test("错误文案:权限/网络异常与后端业务错误分开提示", () => {
|
||||
assert.match(lineActionErrorDetail("网络错误"), /权限/);
|
||||
assert.match(lineActionErrorDetail("Failed to fetch"), /网络异常/);
|
||||
assert.match(lineActionErrorDetail(""), /网络异常/);
|
||||
assert.equal(lineActionErrorDetail("流程线不存在:g1"), "流程线不存在:g1");
|
||||
});
|
||||
|
||||
test("静态接线:Defs.vue 使用线级接口,不混入 listAll 或重复查询", () => {
|
||||
const defs = readFileSync(resolve(process.cwd(), "src/views/flow/admin/Defs.vue"), "utf8");
|
||||
assert.match(defs, /defOfflineLine/);
|
||||
assert.match(defs, /defEnableLine/);
|
||||
assert.match(defs, /lineOffline/);
|
||||
assert.match(defs, /停发流程线/);
|
||||
assert.match(defs, /恢复流程线/);
|
||||
assert.match(defs, /versionActions/);
|
||||
assert.doesNotMatch(defs, /listAll/);
|
||||
});
|
||||
|
||||
test("静态接线:API 客户端使用既有 admin 端点与 flowGuid 请求体", () => {
|
||||
const api = readFileSync(resolve(process.cwd(), "src/api/module/flow.ts"), "utf8");
|
||||
assert.match(api, /`\$\{ROOT\}\/admin\/def\/offlineLine`, \{ flowGuid \}/);
|
||||
assert.match(api, /`\$\{ROOT\}\/admin\/def\/enableLine`, \{ flowGuid \}/);
|
||||
});
|
||||
Reference in New Issue
Block a user