同步远端 PNL0003 诊断和大采样网格能力,语义合并活动感知的 60 秒真停滞判定与旧后端 15 分钟兼容兜底。 纳管热路径优化、15 单元运行证据、浏览器与 API 报告,并补充北京时间更新日志和遗留问题。
253 lines
7.0 KiB
TypeScript
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();
|
|
});
|