Files
SystemSimulationApp/frontend/tests/e2e/simulation-activity-watchdog.spec.ts
T
lujingze e18399c022 整合求解器活动监控与步长回归证据
同步远端 PNL0003 诊断和大采样网格能力,语义合并活动感知的 60 秒真停滞判定与旧后端 15 分钟兼容兜底。

纳管热路径优化、15 单元运行证据、浏览器与 API 报告,并补充北京时间更新日志和遗留问题。
2026-08-19 16:24:31 +00:00

253 lines
7.0 KiB
TypeScript

import { expect, test } from "@playwright/test";
import {
createSimulationActivityWatchdog,
isSimulationSolverActivityPhase,
observeSimulationActivityWatchdog,
} from "../../src/App";
const TEST_STALL_TIMEOUT_MS = 1_000;
test("首个含 activitySequence 的慢步心跳只建立活动基线", () => {
const state = createSimulationActivityWatchdog(0);
const watched = observeSimulationActivityWatchdog(
state,
{ heartbeat: true, activitySequence: 42 },
TEST_STALL_TIMEOUT_MS,
TEST_STALL_TIMEOUT_MS,
);
expect(watched.observation.status).toBe("slow-active");
expect(watched.observation.acceptedProgressIdleMs).toBe(TEST_STALL_TIMEOUT_MS);
expect(watched.observation.activityIdleMs).toBe(0);
});
test("accepted time 停滞但 activitySequence 持续增长时保持慢步运行", () => {
let state = createSimulationActivityWatchdog(0);
({ state } = observeSimulationActivityWatchdog(
state,
{
heartbeat: true,
activitySequence: 1,
acceptedStepSequence: 10,
acceptedTime: 0.048,
},
100,
TEST_STALL_TIMEOUT_MS,
));
let watched = observeSimulationActivityWatchdog(
state,
{
heartbeat: true,
activitySequence: 2,
acceptedStepSequence: 10,
acceptedTime: 0.048,
},
1_000,
TEST_STALL_TIMEOUT_MS,
);
state = watched.state;
expect(watched.observation.status).toBe("slow-active");
watched = observeSimulationActivityWatchdog(
state,
{
heartbeat: true,
activitySequence: 3,
acceptedStepSequence: 10,
acceptedTime: 0.048,
},
2_500,
TEST_STALL_TIMEOUT_MS,
);
expect(watched.observation.status).toBe("slow-active");
expect(watched.observation.acceptedProgressIdleMs).toBe(2_500);
expect(watched.observation.activityIdleMs).toBe(0);
});
test("旧后端缺少活动字段时只报告遥测不可用且不误判 stalled", () => {
const state = createSimulationActivityWatchdog(0);
const watched = observeSimulationActivityWatchdog(
state,
{ heartbeat: true },
20_000,
TEST_STALL_TIMEOUT_MS,
);
expect(watched.observation.status).toBe("slow-telemetry-unavailable");
expect(watched.observation.activityIdleMs).toBeNull();
expect(watched.observation.activityTelemetryAvailable).toBe(false);
});
test("旧后端缺少活动字段时使用 15 分钟兼容兜底", () => {
const state = createSimulationActivityWatchdog(0);
const watched = observeSimulationActivityWatchdog(
state,
{ heartbeat: true },
900_000,
TEST_STALL_TIMEOUT_MS,
);
expect(watched.observation.status).toBe("stalled");
expect(watched.observation.activityTelemetryAvailable).toBe(false);
});
test("接受步和明确可用的活动序号都长期不变时才报告 stalled", () => {
let state = createSimulationActivityWatchdog(0);
({ state } = observeSimulationActivityWatchdog(
state,
{ heartbeat: true, activitySequence: 7 },
0,
TEST_STALL_TIMEOUT_MS,
));
const beforeDeadline = observeSimulationActivityWatchdog(
state,
{ heartbeat: true, activitySequence: 7 },
999,
TEST_STALL_TIMEOUT_MS,
);
expect(beforeDeadline.observation.status).toBe("progressing");
const atDeadline = observeSimulationActivityWatchdog(
beforeDeadline.state,
{ heartbeat: true, activitySequence: 7 },
1_000,
TEST_STALL_TIMEOUT_MS,
);
expect(atDeadline.observation.status).toBe("stalled");
});
test("heartbeat 报告新的 acceptedStepSequence 时重置接受步计时", () => {
let state = createSimulationActivityWatchdog(0);
({ state } = observeSimulationActivityWatchdog(
state,
{
heartbeat: true,
activitySequence: 1,
acceptedStepSequence: 10,
acceptedTime: 0.048,
},
0,
TEST_STALL_TIMEOUT_MS,
));
const watched = observeSimulationActivityWatchdog(
state,
{
heartbeat: true,
activitySequence: 2,
acceptedStepSequence: 11,
acceptedTime: 0.049,
},
5_000,
TEST_STALL_TIMEOUT_MS,
);
expect(watched.observation.status).toBe("progressing");
expect(watched.observation.acceptedProgressIdleMs).toBe(0);
expect(watched.state.lastAcceptedProgressAt).toBe(5_000);
});
test("validation 和 compilation 阶段不参与求解器停滞判定", () => {
let state = createSimulationActivityWatchdog(0);
let watched = observeSimulationActivityWatchdog(
state,
{ phase: "validation", heartbeat: true },
90_000,
TEST_STALL_TIMEOUT_MS,
);
state = watched.state;
expect(watched.observation.status).toBe("progressing");
expect(watched.observation.acceptedProgressIdleMs).toBe(0);
expect(watched.state.integratingPhaseActive).toBe(false);
watched = observeSimulationActivityWatchdog(
state,
{ phase: "compilation", heartbeat: true, activitySequence: 7 },
180_000,
TEST_STALL_TIMEOUT_MS,
);
expect(watched.observation.status).toBe("progressing");
expect(watched.observation.activityIdleMs).toBeNull();
expect(isSimulationSolverActivityPhase("compilation")).toBe(false);
});
test("进入 integrating 时重新建立计时基线", () => {
let state = createSimulationActivityWatchdog(0);
({ state } = observeSimulationActivityWatchdog(
state,
{ phase: "initialization", heartbeat: true },
120_000,
TEST_STALL_TIMEOUT_MS,
));
let watched = observeSimulationActivityWatchdog(
state,
{
phase: "integrating",
heartbeat: true,
activitySequence: 11,
acceptedStepSequence: 3,
acceptedTime: 0.048,
},
180_000,
TEST_STALL_TIMEOUT_MS,
);
state = watched.state;
expect(watched.observation.status).toBe("progressing");
expect(watched.observation.acceptedProgressIdleMs).toBe(0);
expect(watched.observation.activityIdleMs).toBe(0);
expect(watched.state.integratingPhaseActive).toBe(true);
expect(isSimulationSolverActivityPhase("integrating")).toBe(true);
watched = observeSimulationActivityWatchdog(
state,
{
phase: "integrating",
heartbeat: true,
activitySequence: 11,
acceptedStepSequence: 3,
acceptedTime: 0.048,
},
181_000,
TEST_STALL_TIMEOUT_MS,
);
expect(watched.observation.status).toBe("stalled");
});
test("离开 integrating 后清除旧积分阶段的停滞状态", () => {
let state = createSimulationActivityWatchdog(0);
({ state } = observeSimulationActivityWatchdog(
state,
{
phase: "integrating",
heartbeat: true,
activitySequence: 5,
},
0,
TEST_STALL_TIMEOUT_MS,
));
({ state } = observeSimulationActivityWatchdog(
state,
{
phase: "integrating",
heartbeat: true,
activitySequence: 5,
},
TEST_STALL_TIMEOUT_MS,
TEST_STALL_TIMEOUT_MS,
));
const watched = observeSimulationActivityWatchdog(
state,
{ phase: "postprocessing", heartbeat: true },
60_000,
TEST_STALL_TIMEOUT_MS,
);
expect(watched.observation.status).toBe("progressing");
expect(watched.observation.acceptedProgressIdleMs).toBe(0);
expect(watched.state.integratingPhaseActive).toBe(false);
expect(watched.state.lastActivitySequence).toBeNull();
});